diff --git a/.github/workflows/lychee.yml b/.github/workflows/lychee.yml index 4664852761..e5aac51bec 100644 --- a/.github/workflows/lychee.yml +++ b/.github/workflows/lychee.yml @@ -33,6 +33,7 @@ jobs: --exclude 'http://localhost.*' --exclude 'https://localhost.*' --exclude 'https://cockroachlabs.com' + --exclude 'https://docs.permit.io' --exclude 'https://www.gnu.org' --exclude '^/.*' './**/*.md' './**/*.mdx' diff --git a/content/800-guides/410-cloudflare-workers.mdx b/content/800-guides/410-cloudflare-workers.mdx new file mode 100644 index 0000000000..e9d264d6e4 --- /dev/null +++ b/content/800-guides/410-cloudflare-workers.mdx @@ -0,0 +1,438 @@ +--- +title: 'How to use Prisma ORM with Cloudflare Workers' +metaTitle: 'How to use Prisma ORM and Prisma Postgres with Cloudflare Workers' +description: 'Learn how to use Prisma ORM in a Cloudflare Workers project' +sidebar_label: 'Cloudflare Workers' +image: '/img/guides/prisma-cloudflare-workers-cover.png' +completion_time: '15 min' +community_section: true +--- + +## Introduction + +Prisma ORM provides type-safe database access, and [Cloudflare Workers](https://workers.cloudflare.com/) enables you to deploy serverless code at the edge. Together with [Prisma Postgres](https://www.prisma.io/postgres), you get a globally distributed backend with low-latency database access. + +In this guide, you'll learn to integrate Prisma ORM with a Prisma Postgres database in a Cloudflare Workers project. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/cloudflare-workers). + +## Prerequisites +- [Node.js 20+](https://nodejs.org) +- A [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) + +## 1. Set up your project + +Create a new Cloudflare Workers project: + +```terminal +npm create cloudflare@latest prisma-cloudflare-worker -- --type=hello-world --ts=true --git=true --deploy=false +``` + +Navigate into the newly created project directory: + +```terminal +cd prisma-cloudflare-worker +``` + +## 2. Install and configure Prisma + +### 2.1. Install dependencies + +To get started with Prisma, you'll need to install a few dependencies: + +```terminal +npm install prisma dotenv-cli @types/pg --save-dev +npm install @prisma/client @prisma/adapter-pg dotenv pg +``` + +:::info + +If 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](/orm/overview/databases/database-drivers). + +::: + +Once installed, initialize Prisma in your project: + +```terminal +npx prisma init --db +``` + +:::info +You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Cloudflare Workers Project" +::: + +This will create: + +- A `prisma/` directory with a `schema.prisma` file +- A `prisma.config.ts` file with your Prisma configuration +- A `.env` file with a `DATABASE_URL` already set + +### 2.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`: + +```jsonc file=wrangler.jsonc +{ + "name": "prisma-cloudflare-worker", + "main": "src/index.ts", + //add-start + "compatibility_flags": ["nodejs_compat"], + //add-end + "compatibility_date": "2024-01-01" +} +``` + +### 2.3. Add environment types to Prisma config + +Add the `Env` interface to your `prisma.config.ts` to provide type safety for environment variables: + +```typescript file=prisma.config.ts +import "dotenv/config"; +import { defineConfig, env } from "prisma/config"; + +//add-start +export interface Env { + DATABASE_URL: string; +} +//add-end + +import { defineConfig, env } from 'prisma/config'; +export default defineConfig({ + schema: 'prisma/schema.prisma', + migrations: { + path: 'prisma/migrations', + }, + datasource: { + url: env('DATABASE_URL'), + }, +}); +``` + +### 2.4. Define your Prisma schema + +In the `prisma/schema.prisma` file, add the following `User` model and set the runtime to `cloudflare`: + +```prisma file=prisma/schema.prisma +generator client { + provider = "prisma-client" + // add-next-line + runtime = "cloudflare" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" +} + +//add-start +model User { + id Int @id @default(autoincrement()) + email String + name String +} +//add-end +``` + +This creates a `User` model with an auto-incrementing ID, email, and name. + +### 2.5. Configure Prisma scripts + +Add the following scripts to your `package.json` to work with Prisma in the Cloudflare Workers environment: + +```json file=package.json +{ + "scripts": { + //add-start + "migrate": "prisma migrate dev", + "generate": "prisma generate", + "studio": "prisma studio" + //add-end + // ... existing scripts + } +} +``` + +### 2.6. Run migrations and generate Prisma Client + +Now, run the following command to create the database tables: + +```terminal +npm run migrate +``` + +When prompted, name your migration (e.g., `init`). + +Then generate the Prisma Client: + +```terminal +npm run generate +``` + +This 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: + +```typescript file=src/index.ts +//add-start +import { PrismaClient } from './generated/prisma/client'; +import { PrismaPg } from '@prisma/adapter-pg'; + +export interface Env { + DATABASE_URL: string; +} +//add-end + +export default { + async fetch(request, env, ctx): Promise { + return new Response('Hello World!'); + }, +} satisfies ExportedHandler; +``` + +### 3.2. Handle favicon requests + +Add a check to filter out favicon requests, which browsers automatically send and can clutter your logs: + +```typescript file=src/index.ts +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 { + //add-start + const path = new URL(request.url).pathname; + if (path === '/favicon.ico') + return new Response('Resource not found', { + status: 404, + headers: { + 'Content-Type': 'text/plain', + }, + }); + //add-end + + return new Response('Hello World!'); + }, +} satisfies ExportedHandler; +``` + +### 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: + +```typescript file=src/index.ts +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 { + const path = new URL(request.url).pathname; + if (path === '/favicon.ico') + return new Response('Resource not found', { + status: 404, + headers: { + 'Content-Type': 'text/plain', + }, + }); + + //add-start + const adapter = new PrismaPg({ + connectionString: env.DATABASE_URL, + }); + + const prisma = new PrismaClient({ + adapter, + }); + //add-end + + return new Response('Hello World!'); + }, +} satisfies ExportedHandler; +``` + +:::warning + +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: + +```typescript file=src/index.ts +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 { + 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, + }); + + //add-start + 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(); + //add-end + + return new Response('Hello World!'); + }, +} satisfies ExportedHandler; +``` + +### 3.5. Return the results + +Finally, update the response to display the newly created user and the total user count: + +```typescript file=src/index.ts +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 { + 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(); + + //edit-start + return new Response(`\ + Created new user: ${user.name} (${user.email}). + Number of users in the database: ${userCount}. + `); + //edit-end + }, +} satisfies ExportedHandler; +``` + +### 3.6. Test your Worker locally + +First, generate the TypeScript types for your Worker environment: + +```terminal +npx wrangler types --no-strict-vars +``` + +Then start the development server: + +```terminal +npm run dev +``` + +Open [`http://localhost:8787`](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: + +```terminal +npm run studio +``` + +This 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. + +```terminal +npx wrangler secret put DATABASE_URL +``` + +When prompted, paste your database connection string from the `.env` file. + +### 4.2. Deploy your Worker + +Deploy your Worker to Cloudflare: + +```terminal +npm run deploy +``` + +Once 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! + +## Summary + +You've successfully created a Cloudflare Workers application with Prisma ORM connected to a Prisma Postgres database. Your Worker is now running at the edge with low-latency database access. + +## 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](https://hono.dev/) for a more robust routing framework with Cloudflare Workers (see our [Hono guide](/guides/hono)) +- Enable query caching with [Prisma Postgres](/postgres/database/caching) for better performance + +### More info + +- [Prisma Documentation](/orm/overview/introduction) +- [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/) +- [Deploy to Cloudflare](/orm/prisma-client/deployment/edge/deploy-to-cloudflare) diff --git a/sidebars.ts b/sidebars.ts index 34762a3644..0cbaa59b9f 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -376,6 +376,7 @@ const sidebars: SidebarsConfig = { collapsed: false, collapsible: false, items: [ + "guides/cloudflare-workers", "guides/docker", "guides/use-prisma-in-pnpm-workspaces", "guides/data-dog", @@ -428,7 +429,10 @@ const sidebars: SidebarsConfig = { label: "Connection Pooling Guides", collapsed: false, collapsible: false, - items: ["guides/supabase-accelerate", "guides/neon-accelerate"].sort(), + items: [ + "guides/supabase-accelerate", + "guides/neon-accelerate", + ].sort(), }, { type: "category", @@ -480,7 +484,11 @@ const sidebars: SidebarsConfig = { label: "Prompts", collapsed: false, collapsible: false, - items: ["ai/prompts/astro", "ai/prompts/nextjs", "ai/prompts/prisma-7"], + items: [ + "ai/prompts/astro", + "ai/prompts/nextjs", + "ai/prompts/prisma-7", + ], }, { type: "category", @@ -530,7 +538,8 @@ const sidebars: SidebarsConfig = { }, { type: "link", - label: "Vibe Coding an E-commerce App with Prisma MCP and Next.js", + label: + "Vibe Coding an E-commerce App with Prisma MCP and Next.js", href: "https://www.prisma.io/blog/vibe-coding-with-prisma-mcp-and-nextjs", }, { @@ -548,12 +557,14 @@ const sidebars: SidebarsConfig = { items: [ { type: "link", - label: "Automate your workflows with Prisma Postgres and 2,800+ apps", + label: + "Automate your workflows with Prisma Postgres and 2,800+ apps", href: "https://pipedream.com/apps/prisma-management-api", }, { type: "link", - label: "Prompt your application with Firebase Studio & Prisma Postgres", + label: + "Prompt your application with Firebase Studio & Prisma Postgres", href: "/postgres/integrations/idx", }, ],