Skip to content

Commit f230ba4

Browse files
authored
[cloudflare] added some docs for prisma and drizzle (#140)
1 parent 0d450a0 commit f230ba4

File tree

2 files changed

+230
-0
lines changed

2 files changed

+230
-0
lines changed

pages/cloudflare/howtos/_meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"stripeAPI": "Stripe API",
3+
"db": "Database & ORM",
34
"dev-deploy": "Develop and Deploy",
45
"env-vars": "Environment Variables",
56
"image": "Image Optimization",

pages/cloudflare/howtos/db.mdx

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
This page will show you how to setup some popular database ORM libraries to use in OpenNext. There are some subtleties to be aware of when using these libraries in Cloudflare Workers, so we will cover those here.
2+
3+
If you encounter issue with a specific library, please open an issue on the [OpenNext GitHub repository](https://github.com/opennextjs/opennextjs-cloudflare/issues).
4+
5+
## Drizzle ORM
6+
7+
[Drizzle](https://developers.cloudflare.com/d1/reference/community-projects/#drizzle-orm) is a TypeScript ORM for SQL databases. It is designed to be lightweight and easy to use, making it a great choice for Cloudflare Workers.
8+
There is not much specific to configure in Drizzle, but there is one important thing to note is that you don't want to have a global client.
9+
10+
### `lib/db.ts`
11+
12+
Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail.
13+
14+
#### PostgreSQL
15+
16+
Instead of that :
17+
18+
```ts
19+
//lib/db.ts
20+
import { drizzle } from "drizzle-orm/node-postgres";
21+
import * as schema from "./schema/pg";
22+
import { Pool } from "pg";
23+
24+
const pool = new Pool({
25+
connectionString: process.env.PG_URL,
26+
});
27+
28+
export const db = drizzle({ client: pool, schema });
29+
```
30+
31+
You should do this instead:
32+
33+
```ts
34+
//lib/db.ts
35+
import { drizzle } from "drizzle-orm/node-postgres";
36+
// You can use cache from react to cache the client during the same request
37+
// this is not mandatory and only has an effect for server components
38+
import { cache } from "react";
39+
import * as schema from "./schema/pg";
40+
import { Pool } from "pg";
41+
42+
export const getDb = cache(() => {
43+
const pool = new Pool({
44+
connectionString: process.env.PG_URL,
45+
// You don't want to reuse the same connection for multiple requests
46+
maxUses: 1,
47+
});
48+
return drizzle({ client: pool, schema });
49+
});
50+
```
51+
52+
#### D1 example
53+
54+
```ts
55+
import { getCloudflareContext } from "@opennextjs/cloudflare";
56+
import { drizzle } from "drizzle-orm/d1";
57+
import { cache } from "react";
58+
import * as schema from "./schema/d1";
59+
60+
export const getDb = cache(() => {
61+
const { env } = getCloudflareContext();
62+
return drizzle(env.MY_D1, { schema });
63+
});
64+
65+
// This is the one to use for static routes (i.e. ISR/SSG)
66+
export const getDbAsync = cache(async () => {
67+
const { env } = await getCloudflareContext({ async: true });
68+
return drizzle(env.MY_D1, { schema });
69+
});
70+
```
71+
72+
#### Hyperdrive example
73+
74+
```ts
75+
import { getCloudflareContext } from "@opennextjs/cloudflare";
76+
import { drizzle } from "drizzle-orm/node-postgres";
77+
import { cache } from "react";
78+
import * as schema from "./schema/pg";
79+
import { Pool } from "pg";
80+
81+
export const getDb = cache(() => {
82+
const { env } = getCloudflareContext();
83+
const connectionString = env.HYPERDRIVE.connectionString;
84+
const pool = new Pool({
85+
connectionString: process.env.PG_URL,
86+
// You don't want to reuse the same connection for multiple requests
87+
maxUses: 1,
88+
});
89+
return drizzle({ client: pool, schema });
90+
});
91+
92+
// This is the one to use for static routes (i.e. ISR/SSG)
93+
export const getDbAsync = cache(async () => {
94+
const { env } = await getCloudflareContext({ async: true });
95+
const connectionString = env.HYPERDRIVE.connectionString;
96+
const pool = new Pool({
97+
connectionString: process.env.PG_URL,
98+
// You don't want to reuse the same connection for multiple requests
99+
maxUses: 1,
100+
});
101+
return drizzle({ client: pool, schema });
102+
});
103+
```
104+
105+
You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling.
106+
107+
## Prisma ORM
108+
109+
[Prisma](https://developers.cloudflare.com/d1/reference/community-projects/#prisma-orm) is a popular ORM for Node.js and TypeScript. It is designed to be easy to use and provides a lot of features out of the box. However, there are some subtleties to be aware of when using Prisma in Cloudflare Workers.
110+
111+
### `schema.prisma`
112+
113+
When using prisma in OpenNext, you do not want to provide an output directory for the generated client.
114+
115+
```prisma
116+
generator client {
117+
provider = "prisma-client-js"
118+
previewFeatures = ["driverAdapters"]
119+
}
120+
```
121+
122+
This is because the generated client needs to be patched by OpenNext to work with Cloudflare Workers. If you provide an output directory, OpenNext will not be able to patch the client and it will not work.
123+
124+
### `next.config.ts`
125+
126+
Because prisma has some specific exports for cloudflare workers, you need to add the following to your `next.config.ts` file:
127+
128+
```ts
129+
const nextConfig: NextConfig = {
130+
serverExternalPackages: ["@prisma/client", ".prisma/client"],
131+
};
132+
```
133+
134+
By doing this, this will ensure that both the generated client and the prisma client are included in the build for the `workerd` runtime.
135+
136+
### `lib/db.ts`
137+
138+
Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail.
139+
140+
#### D1 example
141+
142+
Instead of that :
143+
144+
```ts
145+
//lib/db.ts
146+
import { getCloudflareContext } from "@opennextjs/cloudflare";
147+
import { PrismaClient } from "@prisma/client";
148+
import { PrismaD1 } from "@prisma/adapter-d1";
149+
150+
const { env } = getCloudflareContext();
151+
const adapter = new PrismaD1(env.MY_D1);
152+
export const db = new PrismaClient();
153+
```
154+
155+
You should do this instead:
156+
157+
```ts
158+
//lib/db.ts
159+
import { getCloudflareContext } from "@opennextjs/cloudflare";
160+
// You can use cache from react to cache the client during the same request
161+
// this is not mandatory and only has an effect for server components
162+
import { cache } from "react";
163+
import { PrismaClient } from "@prisma/client";
164+
import { PrismaD1 } from "@prisma/adapter-d1";
165+
166+
export const getDb = cache(() => {
167+
const { env } = getCloudflareContext();
168+
const adapter = new PrismaD1(env.MY_D1);
169+
return new PrismaClient({ adapter });
170+
});
171+
172+
// If you need access to `getCloudflareContext` in a static route (i.e. ISR/SSG), you should use the async version of `getCloudflareContext` to get the context.
173+
export const getDbAsync = async () => {
174+
const { env } = await getCloudflareContext({ async: true });
175+
const adapter = new PrismaD1(env.MY_D1);
176+
const prisma = new PrismaClient({ adapter });
177+
return prisma;
178+
};
179+
```
180+
181+
You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling.
182+
183+
#### PostgreSQL
184+
185+
You can also use Prisma with PostgreSQL. The setup is similar to the D1 setup above, but you need to use the `PrismaPostgres` adapter instead of the `PrismaD1` adapter.
186+
187+
```ts
188+
import { cache } from "react";
189+
import { PrismaClient } from "@prisma/client";
190+
import { PrismaPg } from "@prisma/adapter-pg";
191+
192+
export const getDb = cache(() => {
193+
const connectionString = process.env.PG_URL ?? "";
194+
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
195+
const prisma = new PrismaClient({ adapter });
196+
return prisma;
197+
});
198+
```
199+
200+
You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling.
201+
202+
#### Hyperdrive
203+
204+
You can also use Prisma with Hyperdrive. The setup is similar to the PostgreSQL setup above.
205+
206+
```ts
207+
//lib/db.ts
208+
import { getCloudflareContext } from "@opennextjs/cloudflare";
209+
// You can use cache from react to cache the client during the same request
210+
// this is not mandatory and only has an effect for server components
211+
import { cache } from "react";
212+
import { PrismaClient } from "@prisma/client";
213+
import { PrismaPg } from "@prisma/adapter-pg";
214+
215+
export const getDb = cache(() => {
216+
const { env } = getCloudflareContext();
217+
const connectionString = env.HYPERDRIVE.connectionString;
218+
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
219+
return new PrismaClient({ adapter });
220+
});
221+
222+
// This is the one to use for static routes (i.e. ISR/SSG)
223+
export const getDbAsync = async () => {
224+
const { env } = await getCloudflareContext({ async: true });
225+
const connectionString = env.HYPERDRIVE.connectionString;
226+
const adapter = new PrismaPg({ connectionString, maxUses: 1 });
227+
return new PrismaClient({ adapter });
228+
};
229+
```

0 commit comments

Comments
 (0)