AlfariziFullstack EngineerYogyakarta, Indonesia

Writing ·

Prisma ORM and Turso (with Next.js 14+ App Router) (English Version)

By combining Prisma and Turso, we can manage and deploy cloud-native SQLite-based Next.js applications.

Originally published in 2024. Examples reflect the versions used at the time. This migrated edition omits old dashboard screenshots and CLI help output, and replaces connection details with safe placeholders.

Prisma ORM offers the advantage of writing schemas simply, and its prisma-client can be used in the backend with type safety. Prisma supports various SQL-based DBMS, one of which is SQLite. When deploying an application, we also need a place to store our SQLite-based data. One DBaaS choice for SQLite is Turso.

In this article, I will share how to use Turso and Prisma with Next.js 14+ (App Router).

Preparing

Before using Turso, we need to set up Next.js and Prisma first.

Installing Next.js

To install Next.js, run the following, then navigate into the application folder:

npx create-next-app my-next-app
cd my-next-app

Installing Prisma

npm install @prisma/client
npm install prisma --save-dev
npx prisma init

Setup Prisma

Define the schema model in /prisma/schema.prisma:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  createdAt DateTime @default(now())
}

Run the migration:

npx prisma migrate dev --name init

The migration named init generates an SQL file at /prisma/migrations/[date]_init/migration.sql and a Prisma Client that can be used to access models with type safety. If the client is not generated, run:

npx prisma generate

Integrating Prisma with Next.js

Create /src/server/db.ts:

import { PrismaClient } from "@prisma/client"

const createPrismaClient = () =>
  new PrismaClient({
    log:
      process.env.NODE_ENV === "development"
        ? ["query", "error", "warn"]
        : ["error"],
  })

const globalForPrisma = globalThis as unknown as {
  prisma: ReturnType<typeof createPrismaClient> | undefined
}

export const db = globalForPrisma.prisma ?? createPrismaClient()

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db

This allows us to call the Prisma client using the db variable.

Turso

Installing Turso CLI

Turso provides a command-line interface for managing databases. The original Homebrew installation command was:

brew install tursodatabase/tap/turso
turso --help

Turso login

Visit Turso and sign in. I used the GitHub provider. Then return to the terminal:

turso auth login

Turso dashboard

In the dashboard, open Databases and choose Create Database. Enter a database name and group. In this example, I named the database testing. After creation, open that database's dashboard.

Connecting Prisma to Turso

Create a local .env file:

TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=

Get the database details with:

turso db show testing

Populate the environment file using your own database URL and token. These are placeholders, not working credentials:

TURSO_DATABASE_URL=libsql://your-database.turso.io
TURSO_AUTH_TOKEN=your-private-token

Never share your auth token or commit your local .env file.

Prisma adapter

In the Prisma version used for this article, add driverAdapters to the generator:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["driverAdapters"]
}

Update /src/server/db.ts to create a LibSQL client and adapter:

import { PrismaClient } from "@prisma/client"
import { PrismaLibSQL } from "@prisma/adapter-libsql"
import { createClient } from "@libsql/client"

const libsql = createClient({
  url: `${process.env.TURSO_DATABASE_URL}`,
  authToken: `${process.env.TURSO_AUTH_TOKEN}`,
})

const adapter = new PrismaLibSQL(libsql)

const createPrismaClient = () =>
  new PrismaClient({
    adapter,
    log:
      process.env.NODE_ENV === "development"
        ? ["query", "error", "warn"]
        : ["error"],
  })

const globalForPrisma = globalThis as unknown as {
  prisma: ReturnType<typeof createPrismaClient> | undefined
}

export const db = globalForPrisma.prisma ?? createPrismaClient()

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db

Run the migration again:

npx prisma migrate dev --name init

Push migration to Turso

The original workflow pushed the generated SQL with the database shell. Replace the database name and migration path with your own:

turso db shell testing < ./prisma/migrations/20240902093411_init/migration.sql

Return to the dashboard and inspect the data with the available database editor. The Post table should now be present. If the database is inactive, check its status and settings in the dashboard.

Conclusion

By combining Prisma and Turso, we can manage and deploy cloud-native Next.js applications based on SQLite. Turso provides cloud-hosted SQLite, while Prisma helps manage database schemas with type safety in TypeScript.