September 28, 2023

SQLite on the Edge: Prisma Support for Turso is in Early Access

Turso is an edge-hosted, distributed database that's based on libSQL, an open-source and open-contribution fork of SQLite, enabling you to bring data closer to your application and minimize query latency.

We’re excited to share that Prisma ORM is adding Early Access support for Turso. Let’s dive in!

What is Turso, and how is it different from SQLite?

SQLite is a self-contained, file-based open-source database known for its portability, reliability, and performance, even in environments with low memory. It’s also the perfect fit for small web applications because of its speed.

However, scaling SQLite introduces challenges:

  • Manual backups
  • Lack of out-of-the-box replication
  • Difficulties persisting data in serverless environments
  • Hosting difficulties in multi-server setups

Some of these challenges can be solved by tools such as LiteFS, which provides replication and database backups for SQLite.

On the other hand, Turso solved the above challenges by creating libSQL — a fork of SQLite that adds features that SQLite does not support yet. libSQL allows you to distribute and replicate SQLite, connect over HTTP, perform async operations, and embed SQLite as part of other programs as a primary database or replica of the primary database.

Prisma + Turso = 🚀

While Prisma has supported SQLite from its first release in 2019, libSQL differs from SQLite. For example, libSQL uses HTTP to connect to the database and uses a remote file over a local one, making Prisma and Turso incompatible until now.

Today, we’re excited to announce that Prisma support for Turso is now available in Early Access!

import { PrismaClient } from '@prisma/client'
import { PrismaLibSQL } from '@prisma/adapter-libsql'
import { createClient } from '@libsql/client'
// Create a new instance of the libSQL database client
const libsql = createClient({
// @ts-expect-error
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN
})
// Create a Prisma "adapter" for libSQL
const adapter = new PrismaLibSQL(libsql)
// Pass the adapter option to the Prisma Client instance
const prisma = new PrismaClient({ adapter })
export default prisma

Get started with Prisma and Turso

To start using Turso in your project, you must first enable the driverAdapters Preview feature flag. This will allow you to query your database using Turso’s driver adapter.

The driverAdapters feature flag is part of the driver adapter initiative we're working on to enable you use other database drivers to connect to your database. Example driver adapters include PlanetScale, Neon, and libSQL. We’ll share more details soon! If you have not yet, fill out this survey, and leave us your email address for updates.

Prerequisites

You will need to have the following tools installed:

You can set up a project using try-prisma if you don’t have an existing project using SQLite. Navigate to your working directory and copy the command below to set up the project:

npx try-prisma@latest --template typescript/script --path . --name turso-prisma --install npm
Copy

Navigate to the project and open it on your preferred code editor:

cd turso-prisma
Copy

Create a database on Turso

First, create a database on Turso that your application will use. This step is necessary for creating the credentials required to configure Turso’s database client.

  1. To create a database, run the following command on your terminal:

    turso db create turso-prisma
    Copy

    Turso will also create a database in the region closest to your location.

  2. Create an authentication token that will allow you to connect to the database:

    turso db tokens create turso-prisma
    Copy
  3. Next, reveal the connection string details to connect to your database:

    turso db show turso-prisma
    Copy

    Take note of the authentication token and connection string which will be used to connect to your database in the next step.

Connect to Turso using Prisma

To get started using Turso:

  1. Enable the driverAdapters Preview feature flag in your Prisma schema:

    generator client {
    provider = "prisma-client-js"
    + previewFeatures = ["driverAdapters"]
    }
    datasource db {
    provider = "sqlite"
    url = "file:./dev.db"
    }
    Copy
  2. Create or update your .env file with the environment variables with the values from the “Create a database on Turso” step:

    TURSO_AUTH_TOKEN="eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
    TURSO_DATABASE_URL="libsql://turso-prisma-random-user.turso.io"
    Copy
  3. Create the initial migration:

    npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > migration.sql
    Copy
  4. Apply the migration to your Turso database:

    turso db shell turso-prisma < migration.sql
    Copy
  5. Install the latest version of Prisma Client:

    npm install @prisma/client@latest
    Copy
  6. Install the libSQL database client and the driver adapter for Prisma Client:

    npm install @prisma/adapter-libsql
    npm install @libsql/client
    Copy
  7. Update your Prisma Client instance with the following snippet:

    import { PrismaClient } from '@prisma/client'
    // 1. Import libSQL and the Prisma libSQL driver adapter
    import { PrismaLibSQL } from '@prisma/adapter-libsql'
    import { createClient } from '@libsql/client'
    // 2. Instantiate libSQL
    const libsql = createClient({
    // @ts-expect-error
    url: process.env.TURSO_DATABASE_URL,
    authToken: process.env.TURSO_AUTH_TOKEN
    })
    // 3. Instantiate the libSQL driver adapter
    const adapter = new PrismaLibSQL(libsql)
    // Pass the adapter option to the Prisma Client instance
    const prisma = new PrismaClient({ adapter })
    Copy

And that’s it!

You can now start querying your Turso database using Prisma Client.

If you cloned the typescript/script example, you can run npm run dev to insert and query data to confirm your changes were successful.

Where to go from here

The setup above uses a single remote database. You can take it a step further by setting up database replicas. Turso automatically picks the closest replica to your app for read queries when you create replicas. No additional logic is required to define how the routing of the read queries should be handled. Write queries will be forwarded to the primary database.

Try it out and share your feedback! We encourage you to create an issue if you find something missing or run into a bug.


Beyond remote SQLite: embedded replicas

While Turso allows you to replicate SQLite globally, what if you could eliminate the extra network hop from your app to the remote replica? What if… you could move the database inside your application?

With Turso's newly announced embedded replicas, you can have a copy of your primary, remote database inside your application, similar to how an embedded/local SQLite database works. You can try this out in your application using Prisma’s new support for Turso.

A meme gif of a person having their mind blown.

How do embedded replicas work?

When your app initially establishes a connection to your database, the remote primary database will fulfill the query:

Then Turso will (1) create an embedded replica inside your application and (2) copy data from your primary database to the replica so it is locally available:

The embedded replica will fulfill subsequent read queries. The libSQL client provides a sync() method which you can invoke to ensure the embedded replica's data remains fresh.

With embedded replicas, this setup guarantees your app is fast because the data will be readily available locally.

Like a read replica setup you may be familiar with, write operations are forwarded to the primary remote database and executed before being propagated to all embedded replicas.

  1. Write operations propagation are forwarded to the database.
  2. Database responds to the server with the updates from 1.
  3. Write operations are propagated to the database replica.

Impact of embedded replicas on your queries

To demonstrate the speed of embedded replicas, we created two example apps using the same primary database - and one variant uses an embedded replica.

For this sample test, the following query will be used:

await prisma.post.findMany({
where: { published: true },
select: {
id: true,
title: true,
author: true // relation
}
})

Below are screenshots of the timings for the same query on application startup:

Without an embedded replica:

Time: 154.390 ms

With an embedded replica:

Time: 7.883 ms

The query response significantly drops from 154.390 ms to 7.883 ms.

If you would like to try it out yourself, you can find the two example apps up on GitHub:

What can you use an embedded replica for?

Embedded replicas are a relatively new feature of Turso, but some of the use cases you could use them for include:

  • Improving read performance of your APIs: Embedded replicas can eliminate the network cost of connecting to a remote database server with an embedded database with Prisma Client.
  • Replacement for your caching service: Embedded replicas can be paired with a Prisma Client extension for query responses, making your app faster because the data is always kept fresh.

It will be interesting to see what other use cases you will come up with for this new approach to database replicas.

Try it out yourself!

We can't wait to see what you build with Turso and Prisma. We encourage you to try it out and let us know your thoughts.

We’re also working on supporting other serverless database drivers and edge function deployments. Take this short survey and leave us your email address for updates.

Be sure to share with us what you build on Twitter or Discord. 🙌

Don’t miss the next post!

Sign up for the Prisma Newsletter

Key takeaways from the Discover Data DX virtual event

December 13, 2023

Explore the insights from the Discover Data DX virtual event held on December 7th, 2023. The event brought together industry leaders to discuss the significance and principles of the emerging Data DX category.

Prisma Accelerate now in General Availability

October 26, 2023

Now in General Availability: Dive into Prisma Accelerate, enhancing global database connections with connection pooling and edge caching for fast data access.

Support for Serverless Database Drivers in Prisma ORM Is Now in Preview

October 06, 2023

Prisma is releasing Preview support for serverless database drivers from Neon and PlanetScale. This feature allows Prisma users to leverage the existing database drivers for communication with their database without long-lived TCP connections!