Turso
本指南讨论了使用 Prisma ORM 和 Turso 的概念,解释了 Turso 与其他数据库提供商之间的异同,并引导您完成配置应用程序以集成 Turso 的过程。
Prisma ORM 对 Turso 的支持目前处于抢先体验阶段。我们非常感谢您在此 GitHub 讨论中提供反馈。
什么是 Turso?
Turso是一个基于libSQL(一个开源且开放贡献的SQLite分支)的边缘托管分布式数据库,能够将数据更接近您的应用程序并最大程度地减少查询延迟。Turso 也可以托管在远程服务器上。
从 Prisma ORM 5.4.2 版本及更高版本开始,对 Turso 的支持在抢先体验阶段可用。
与其他数据库提供商的共同点
libSQL 与 SQLite 100% 兼容。libSQL 扩展了 SQLite 并添加了以下特性和功能:
- 支持复制
- 支持自动备份
- 能够将 Turso 嵌入到其他程序中,例如 Linux 内核
- 支持用户定义函数
- 支持异步 I/O
要了解更多关于 libSQL 与 SQLite 差异的信息,请参阅libSQL 宣言。
使用 Prisma ORM 与 Turso 的许多方面都与使用 Prisma ORM 与其他关系型数据库类似。您仍然可以:
- 使用Prisma Schema Language建模您的数据库
- 在您的 schema 中使用 Prisma ORM 现有的
sqlite
数据库连接器 - 在您的应用程序中使用Prisma Client与 Turso 的数据库服务器进行通信
需要考虑的差异
Turso 和 SQLite 之间存在一些差异需要考虑。在决定使用 Turso 和 Prisma ORM 时,您应该注意以下几点:
- 远程和嵌入式 SQLite 数据库。libSQL 使用 HTTP 连接到远程 SQLite 数据库。libSQL 还支持远程数据库副本和嵌入式副本。嵌入式副本使您能够在应用程序内部复制主数据库。
- 进行模式更改。由于 libSQL 使用 HTTP 连接到远程数据库,这使其与 Prisma Migrate 不兼容。但是,您可以使用
prisma migrate diff
创建模式迁移,然后使用Turso 的 CLI将更改应用到数据库。
如何连接和查询 Turso 数据库
以下部分介绍了如何创建 Turso 数据库、检索数据库凭据并连接到数据库。
如何配置数据库并检索数据库凭据
确保您已安装Turso CLI来管理您的数据库。
如果您没有现有数据库,可以通过运行以下命令来配置数据库:
turso db create turso-prisma-db
上述命令将在离您位置最近的区域创建一个数据库。
运行以下命令以检索您的数据库连接字符串:
turso db show turso-prisma-db
接下来,创建一个身份验证令牌,允许您连接到数据库:
turso db tokens create turso-prisma-db
使用身份验证令牌和连接字符串更新您的.env
文件
TURSO_AUTH_TOKEN="eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
TURSO_DATABASE_URL="libsql://turso-prisma-db-user.turso.io"
如何连接 Turso 数据库
首先,启用 driverAdapters
预览功能标志:
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "sqlite"
url = "file:./dev.db" // will be ignored
}
生成 Prisma Client
npx prisma generate
安装 libSQL 的 Prisma ORM 驱动适配器包
npm install @prisma/adapter-libsql
更新您的 Prisma Client 实例
import { PrismaClient } from '@prisma/client'
import { PrismaLibSQL } from '@prisma/adapter-libsql'
const adapter = new PrismaLibSQL({
url: `${process.env.TURSO_DATABASE_URL}`,
authToken: `${process.env.TURSO_AUTH_TOKEN}`,
})
const prisma = new PrismaClient({ adapter })
您可以像往常一样在项目中使用 Prisma Client,并享受完整的类型安全。
在 prisma.config.ts
中通过驱动适配器使用 Prisma Migrate(抢先体验)
从v6.6.0开始,通过 prisma.config.ts
文件,您可以使用 prisma db push
对数据库模式进行更改。
1. 安装 LibSQL 驱动适配器
在您的终端中运行此命令:
npm install @prisma/adapter-libsql
2. 设置环境变量
为了设置 LibSQL 适配器,您需要向 .env
文件添加一些密钥:
LIBSQL_DATABASE_URL
:您的 Turso 数据库实例的连接 URL。LIBSQL_DATABASE_TOKEN
:您的 Turso 数据库实例的令牌。
然后,您可以将这些添加到您的 .env
文件中,或者如果它们存储在不同的密钥存储中,则直接使用它们:
LIBSQL_DATABASE_URL="..."
LIBSQL_DATABASE_TOKEN="..."
3. 设置 Prisma 配置文件
确保您的项目有一个prisma.config.ts
文件。然后,设置迁移驱动适配器以使用PrismaLibSQL
import path from 'node:path'
import { defineConfig } from 'prisma/config'
import { PrismaLibSQL } from '@prisma/adapter-libsql'
// import your .env file
import 'dotenv/config'
type Env = {
LIBSQL_DATABASE_URL: string
LIBSQL_DATABASE_TOKEN: string
}
export default defineConfig<Env>({
earlyAccess: true,
schema: path.join('prisma', 'schema.prisma'),
migrate: {
async adapter(env) {
return new PrismaLibSQL({
url: env.LIBSQL_DATABASE_URL,
authToken: env.LIBSQL_DATABASE_TOKEN,
})
}
}
})
4. 迁移您的数据库
现在,Prisma Migrate 将根据 prisma.config.ts
中提供的配置,对您的远程 Turso 数据库运行迁移。
要使用此工作流创建第一次迁移,请运行以下命令:
npx prisma db push
嵌入式 Turso 数据库副本
Turso 支持嵌入式副本。Turso 的嵌入式副本使您可以在应用程序内部拥有主远程数据库的副本。嵌入式副本的行为类似于本地 SQLite 数据库。由于数据库在您的应用程序内部,因此数据库查询速度更快。
嵌入式数据库副本的工作原理
当您的应用程序最初与数据库建立连接时,主数据库将完成查询
Turso 将 (1) 在您的应用程序内部创建一个嵌入式副本,并 (2) 将数据从主数据库复制到副本,以便在本地可用
嵌入式副本将满足后续的读取查询。libSQL 客户端提供了sync()
方法,您可以调用该方法以确保嵌入式副本的数据保持最新。
通过嵌入式副本,此设置保证了响应迅速的应用程序,因为数据将随时在本地可用且访问速度更快。
就像您可能熟悉的那样,写入操作被转发到主远程数据库并执行,然后传播到所有嵌入式副本。
- 写入操作的传播被转发到数据库。
- 数据库以来自 1 的更新响应服务器。
- 写入操作传播到数据库副本。
您应用程序的数据需求将决定您应该多久在远程数据库和嵌入式数据库副本之间同步数据。例如,您可以使用中间件函数(例如 Express 和 Fastify)或 cron 作业来同步数据。
如何同步远程数据库和嵌入式副本之间的数据
要开始将嵌入式副本与 Prisma ORM 一起使用,请在您的应用程序中添加 libSQL 的 sync()
方法。下面的示例展示了如何使用 Express 中间件同步数据。
import express from 'express'
const app = express()
// ... the rest of your application code
app.use(async (req, res, next) => {
await libsql.sync()
next()
})
app.listen(3000, () => console.log(`Server ready at http://localhost:3000`))
它也可以作为Prisma Client 扩展实现。下面的示例展示了在执行创建、更新或删除操作后自动同步。
const prisma = new PrismaClient().$extends({
query: {
$allModels: {
async $allOperations({ operation, model, args, query }) {
const result = await query(args)
// Synchronize the embedded replica after any write operation
if (['create', 'update', 'delete'].includes(operation)) {
await libsql.sync()
}
return result
}
}
}
})