本页将向您展示如何在 OpenNext 中设置一些流行的数据库 ORM 库。在 Cloudflare Workers 中使用这些库时需要注意一些细节,我们将在此进行说明。
如果您遇到特定库的问题,请在 OpenNext GitHub 仓库 (opens in a new tab) 提交 issue。
Drizzle ORM
Drizzle (opens in a new tab) 是一个面向 SQL 数据库的 TypeScript ORM。它设计轻量且易于使用,非常适合 Cloudflare Workers。 Drizzle 不需要太多特殊配置,但有一点非常重要:不要使用全局客户端。
lib/db.ts
您应该为每个请求创建新的客户端,而不是使用全局客户端。这是因为某些适配器(如 Postgres)会使用连接池,并为多个请求复用同一个连接。这在 Cloudflare Workers 中是不允许的,会导致后续请求失败。
PostgreSQL
不要这样做:
//lib/db.ts
import { drizzle } from "drizzle-orm/node-postgres";
import * as schema from "./schema/pg";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.PG_URL,
});
export const db = drizzle({ client: pool, schema });
而应该这样做:
//lib/db.ts
import { drizzle } from "drizzle-orm/node-postgres";
// 您可以使用 react 的 cache 来在同一请求期间缓存客户端
// 这不是强制性的,且仅对服务器组件有效
import { cache } from "react";
import * as schema from "./schema/pg";
import { Pool } from "pg";
export const getDb = cache(() => {
const pool = new Pool({
connectionString: process.env.PG_URL,
// 您不希望为多个请求复用同一个连接
maxUses: 1,
});
return drizzle({ client: pool, schema });
});
D1 示例
import { getCloudflareContext } from "@opennextjs/cloudflare";
import { drizzle } from "drizzle-orm/d1";
import { cache } from "react";
import * as schema from "./schema/d1";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
return drizzle(env.MY_D1, { schema });
});
// 这个函数用于静态路由(如 ISR/SSG)
export const getDbAsync = cache(async () => {
const { env } = await getCloudflareContext({ async: true });
return drizzle(env.MY_D1, { schema });
});
Hyperdrive 示例
import { getCloudflareContext } from "@opennextjs/cloudflare";
import { drizzle } from "drizzle-orm/node-postgres";
import { cache } from "react";
import * as schema from "./schema/pg";
import { Pool } from "pg";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
const connectionString = env.HYPERDRIVE.connectionString;
const pool = new Pool({
connectionString: process.env.PG_URL,
// 不建议为多个请求复用同一个连接
maxUses: 1,
});
return drizzle({ client: pool, schema });
});
// 这个函数用于静态路由(如 ISR/SSG)
export const getDbAsync = cache(async () => {
const { env } = await getCloudflareContext({ async: true });
const connectionString = env.HYPERDRIVE.connectionString;
const pool = new Pool({
connectionString: process.env.PG_URL,
// 不建议为多个请求复用同一个连接
maxUses: 1,
});
return drizzle({ client: pool, schema });
});
你可以使用 getDb
函数为每个请求获取一个新的客户端。这将确保你不会遇到连接池相关的问题。
Prisma ORM
Prisma (opens in a new tab) 是一个流行的 Node.js 和 TypeScript ORM(对象关系映射)工具。它设计简洁易用,并提供了开箱即用的丰富功能。不过在 Cloudflare Workers 中使用 Prisma 时需要注意一些细节。
schema.prisma
在 OpenNext 中使用 Prisma 时,不需要为生成的客户端指定输出目录。
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
这是因为生成的客户端需要由 OpenNext 进行修补才能与 Cloudflare Workers 兼容。如果指定了输出目录,OpenNext 将无法修补客户端,导致其无法正常工作。
next.config.ts
由于 Prisma 对 Cloudflare Workers 有特定的导出配置,你需要在 next.config.ts
文件中添加以下内容:
const nextConfig: NextConfig = {
serverExternalPackages: ["@prisma/client", ".prisma/client"],
};
这样做可以确保生成的客户端和 Prisma 客户端都被包含在 workerd
运行时的构建中。
lib/db.ts
应该为每个请求创建新的客户端实例,而不是使用全局客户端。这是因为某些适配器(如 Postgres)会使用连接池,并在多个请求间复用同一个连接。这在 Cloudflare Workers 中是不允许的,会导致后续请求失败。
D1 示例
不要这样做:
//lib/db.ts
import { getCloudflareContext } from "@opennextjs/cloudflare";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
const { env } = getCloudflareContext();
const adapter = new PrismaD1(env.MY_D1);
export const db = new PrismaClient();
而应该这样做:
//lib/db.ts
import { getCloudflareContext } from "@opennextjs/cloudflare";
// 可以使用 react 的 cache 来在同一个请求期间缓存客户端
// 这不是强制性的,且仅对服务器组件有效
import { cache } from "react";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
const adapter = new PrismaD1(env.MY_D1);
return new PrismaClient({ adapter });
});
// 如果需要在静态路由(如 ISR/SSG)中访问 `getCloudflareContext`,应该使用异步版本的 `getCloudflareContext` 来获取上下文
export const getDbAsync = async () => {
const { env } = await getCloudflareContext({ async: true });
const adapter = new PrismaD1(env.MY_D1);
const prisma = new PrismaClient({ adapter });
return prisma;
};
然后你可以使用 getDb
函数为每个请求获取一个新的客户端。这将确保你不会遇到连接池相关的任何问题。
PostgreSQL
你也可以将 Prisma 与 PostgreSQL 配合使用。配置方式与上述 D1 的设置类似,但需要使用 PrismaPostgres
适配器而非 PrismaD1
适配器。
import { cache } from "react";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export const getDb = cache(() => {
const connectionString = process.env.PG_URL ?? "";
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
const prisma = new PrismaClient({ adapter });
return prisma;
});
之后你可以使用 getDb
函数为每个请求获取一个新的客户端。这将确保你不会遇到任何连接池相关的问题。
Hyperdrive
你也可以将 Prisma 与 Hyperdrive 配合使用。配置方式与上述 PostgreSQL 的设置类似。
//lib/db.ts
import { getCloudflareContext } from "@opennextjs/cloudflare";
// 你可以使用 react 的 cache 来在同一请求期间缓存客户端
// 这不是强制性的,且仅对服务器组件有效
import { cache } from "react";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
const connectionString = env.HYPERDRIVE.connectionString;
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
return new PrismaClient({ adapter });
});
// 这个函数用于静态路由(即 ISR/SSG)
export const getDbAsync = async () => {
const { env } = await getCloudflareContext({ async: true });
const connectionString = env.HYPERDRIVE.connectionString;
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
return new PrismaClient({ adapter });
};