Cloudflare Workers
Learn how to use Prisma ORM and Prisma Postgres in a Cloudflare Workers project
Introduction
Yes, Prisma Postgres works on Cloudflare Workers.
Standard PostgreSQL clients (like pg or postgres.js) use TCP connections, and Cloudflare Workers doesn't support TCP by default. That's why most Postgres databases can't be accessed directly from Workers. There are two ways around it with Prisma Postgres:
- Node.js compatibility mode — add the
nodejs_compatflag to yourwrangler.jsoncand Prisma ORM can use TCP via Cloudflare's Node.js TCP API. That's what this guide does. - Serverless HTTP driver — the
@prisma/ppgdriver connects over HTTP instead of TCP. No compatibility flag needed.
This guide uses the nodejs_compat approach. Prisma Postgres handles connection pooling, so you don't have to worry about Workers spinning up a new connection per request.
You can find a complete example on GitHub.
Prerequisites
1. Set up your project
Create a new Cloudflare Workers project:
bunx create-cloudflare prisma-cloudflare-worker --type hello-world --ts=true --git=true --deploy=falseNavigate into the newly created project directory:
cd prisma-cloudflare-worker2. Install and configure Prisma
2.1. Install dependencies
To get started with Prisma, you'll need to install a few dependencies:
bun add prisma dotenv-cli @types/pg --devbun add @prisma/client @prisma/adapter-pg dotenv pgIf you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of @prisma/adapter-pg. For more information, see Database drivers.
Once installed, initialize Prisma in your project:
bunx --bun prisma initprisma init creates the Prisma scaffolding and a local DATABASE_URL. In the next step, you will create a Prisma Postgres database and replace that value with a direct postgres://... connection string.
This will create:
- A
prisma/directory with aschema.prismafile - A
prisma.config.tsfile with your Prisma configuration - A
.envfile with a localDATABASE_URLalready set
Create a Prisma Postgres database and replace the generated DATABASE_URL in your .env file with the postgres://... connection string from the CLI output:
bunx create-db2.2. Enable Node.js compatibility in Cloudflare Workers
Cloudflare Workers needs Node.js compatibility enabled to work with Prisma. Add the nodejs_compat compatibility flag to your wrangler.jsonc:
{
"name": "prisma-cloudflare-worker",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
"compatibility_date": "2024-01-01"
}2.3. Define your Prisma Schema
In the prisma/schema.prisma file, add the following User model and set the runtime to cloudflare:
generator client {
provider = "prisma-client"
runtime = "cloudflare"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model User {
id Int @id @default(autoincrement())
email String
name String
} Both the cloudflare and workerd runtimes are supported. Read more about runtimes here.
This creates a User model with an auto-incrementing ID, email, and name.
2.4. Configure Prisma scripts
Add the following scripts to your package.json to work with Prisma in the Cloudflare Workers environment:
{
"scripts": {
"migrate": "prisma migrate dev",
"generate": "prisma generate",
"studio": "prisma studio"
// ... existing scripts
}
}2.5. Run migrations and generate Prisma Client
Now, run the following command to create the database tables:
bun run migrateWhen prompted, name your migration (e.g., init).
Then generate the Prisma Client:
bun run generateThis generates the Prisma Client in the src/generated/prisma/client directory.
3. Integrate Prisma into Cloudflare Workers
3.1. Import Prisma Client and configure types
At the top of src/index.ts, import the generated Prisma Client and the PostgreSQL adapter, and define the Env interface for type-safe environment variables:
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
return new Response("Hello World!");
},
} satisfies ExportedHandler<Env>;3.2. Handle favicon requests
Add a check to filter out favicon requests, which browsers automatically send and can clutter your logs:
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const path = new URL(request.url).pathname;
if (path === "/favicon.ico")
return new Response("Resource not found", {
status: 404,
headers: {
"Content-Type": "text/plain",
},
});
return new Response("Hello World!");
},
} satisfies ExportedHandler<Env>;3.3. Initialize the Prisma Client
Create a database adapter and initialize Prisma Client with it. This must be done for each request in edge environments:
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const path = new URL(request.url).pathname;
if (path === "/favicon.ico")
return new Response("Resource not found", {
status: 404,
headers: {
"Content-Type": "text/plain",
},
});
const adapter = new PrismaPg({
connectionString: env.DATABASE_URL,
});
const prisma = new PrismaClient({
adapter,
});
return new Response("Hello World!");
},
} satisfies ExportedHandler<Env>;In edge environments like Cloudflare Workers, you create a new Prisma Client instance per request. This is different from long-running Node.js servers where you typically instantiate a single client and reuse it.
3.4. Create a user and query the database
Now use Prisma Client to create a new user and count the total number of users:
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const path = new URL(request.url).pathname;
if (path === "/favicon.ico")
return new Response("Resource not found", {
status: 404,
headers: {
"Content-Type": "text/plain",
},
});
const adapter = new PrismaPg({
connectionString: env.DATABASE_URL,
});
const prisma = new PrismaClient({
adapter,
});
const user = await prisma.user.create({
data: {
email: `Prisma-Postgres-User-${Math.ceil(Math.random() * 1000)}@gmail.com`,
name: "Jon Doe",
},
});
const userCount = await prisma.user.count();
return new Response("Hello World!");
},
} satisfies ExportedHandler<Env>;3.5. Return the results
Finally, update the response to display the newly created user and the total user count:
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
export interface Env {
DATABASE_URL: string;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const path = new URL(request.url).pathname;
if (path === "/favicon.ico")
return new Response("Resource not found", {
status: 404,
headers: {
"Content-Type": "text/plain",
},
});
const adapter = new PrismaPg({
connectionString: env.DATABASE_URL,
});
const prisma = new PrismaClient({
adapter,
});
const user = await prisma.user.create({
data: {
email: `Prisma-Postgres-User-${Math.ceil(Math.random() * 1000)}@gmail.com`,
name: "Jon Doe",
},
});
const userCount = await prisma.user.count();
return new Response(`\
Created new user: ${user.name} (${user.email}). // [!code highlight]
Number of users in the database: ${userCount}. // [!code highlight]
`);
},
} satisfies ExportedHandler<Env>;3.6. Test your Worker locally
First, generate the TypeScript types for your Worker environment:
bunx wrangler types --no-strict-varsThen start the development server:
bun run devOpen http://localhost:8787 in your browser. Each time you refresh the page, a new user will be created. You should see output similar to:
Created new user: Jon Doe (Prisma-Postgres-User-742@gmail.com).
Number of users in the database: 5.3.7. Inspect your data with Prisma Studio
To view your database contents, open Prisma Studio:
bun run studioThis will open a browser window where you can view and edit your User table data.
4. Deploy to Cloudflare Workers
4.1. Set your database URL as a secret
Before deploying, you need to set your DATABASE_URL as a secret in Cloudflare Workers. This keeps your database connection string secure in production.
bunx wrangler secret put DATABASE_URLWhen prompted, paste your database connection string from the .env file.
4.2. Deploy your Worker
Deploy your Worker to Cloudflare:
bun run deployOnce deployed, Cloudflare will provide you with a URL where your Worker is live (e.g., https://prisma-postgres-worker.your-subdomain.workers.dev).
Visit the URL in your browser, and you'll see your Worker creating users in production!
Next steps
Now that you have a working Cloudflare Workers app connected to a Prisma Postgres database, you can:
- Add routes to handle different HTTP methods (GET, POST, PUT, DELETE)
- Extend your Prisma schema with more models and relationships
- Implement authentication and authorization
- Use Hono for a more robust routing framework with Cloudflare Workers (see our Hono guide)
- Use the
@prisma/ppgserverless driver to connect over HTTP instead of TCP — nonodejs_compatflag needed - Deploy to Cloudflare — deeper reference on edge deployment options
- Cloudflare Workers documentation
- Prisma ORM documentation