{"content":"# Neon + Drizzle Setup\n\nConnect a Next.js app to Neon Postgres using Drizzle ORM with optimized connection pooling for Vercel serverless functions.\n\n### Step 1: Add the Neon MCP Server\n\n```bash\nbunx add-mcp https://mcp.neon.tech/mcp -y\n```\n\nThis updates all detected agents automatically. If no agents are detected, add `-a opencode -a cursor` to the command or prompt the user to specify what agents they want to use for this project.\n\n### Step 2: Create a new Neon project\n\nUse an existing Neon project or create a new one, either through the [Neon Dashboard](https://console.neon.tech/) or by instructing your coding agent to create a new project or retrieve the connection string of an existing project.\n\n### Step 3: Get your Neon database URL\n\n1. Go to the [Neon Dashboard](https://console.neon.tech/)\n2. Select your project\n3. Copy the connection string from the **Connection Details** widget\n4. Add it to your `.env.development`:\n\n```env\nDATABASE_URL=\"postgresql://user:password@ep-xxx.region.aws.neon.tech/neondb?sslmode=require\"\n```\n\nThen sync to Vercel with `bun run env:push`. See [Environment Variable Management](/recipes/env-management) for the full setup.\n\n> **Tip**: Use the **pooled** connection string for production workloads to improve performance and handle more concurrent connections.\n\n### Step 4: Create the database config\n\nInstead of accessing `process.env.DATABASE_URL` directly, use the type-safe config pattern:\n\n```typescript\n// src/lib/db/config.ts\nimport { configSchema, server } from \"better-env/config-schema\";\n\nexport const databaseConfig = configSchema(\"Database\", {\n  url: server({ env: \"DATABASE_URL\" }),\n});\n```\n\nThen access via `databaseConfig.server.url` instead of `process.env.DATABASE_URL`. See the [Environment Variable Management](/recipes/env-management) recipe for the full pattern.\n\n### Step 5: Validate config on server start\n\nImport the config in `instrumentation.ts` to validate the environment variable when the server starts:\n\n```typescript\n// src/instrumentation.ts\n\n// Validate required configs on server start\nimport \"./lib/db/config\";\n```\n\nThis ensures the server fails immediately on startup if `DATABASE_URL` is missing, rather than failing later when a database query runs.\n\n### Step 6: Install packages\n\n```bash\nbun add drizzle-orm pg @vercel/functions\nbun add -D drizzle-kit @types/pg @next/env\n```\n\nThe `@next/env` package loads environment variables in the same order as Next.js, ensuring your `.env.development` and `.env.local` variables are available when running Drizzle Kit commands outside of the Next.js runtime.\n\n### Step 7: Create the database client\n\nCreate the Drizzle database client:\n\n```typescript\n// src/lib/db/client.ts\nimport { attachDatabasePool } from \"@vercel/functions\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { Pool } from \"pg\";\nimport { databaseConfig } from \"./config\";\n\n// Replace with your app's schemas\nimport * as authSchema from \"@/lib/auth/schema\";\nimport * as chatSchema from \"@/lib/chat/schema\";\n\nconst schema = {\n  ...authSchema,\n  ...chatSchema,\n};\n\nconst pool = new Pool({\n  connectionString: databaseConfig.server.url,\n});\nattachDatabasePool(pool);\n\nconst db = drizzle({ client: pool, schema });\n\nexport { db };\n```\n\nThe `databaseConfig` import provides type-safe access to the `DATABASE_URL` environment variable. See the [Environment Variable Management](/recipes/env-management) recipe for the config setup pattern.\n\nEach feature library owns its own schema file (e.g., `@/lib/auth/schema`, `@/lib/chat/schema`). Instead of a central `db/schema.ts` aggregation file, schemas are imported directly in `client.ts` and merged into a single object for type-safe queries.\n\n### Step 8: Configure Drizzle Kit\n\nCreate the Drizzle Kit configuration in your project root:\n\n```typescript\n// drizzle.config.ts\nimport { loadEnvConfig } from \"@next/env\";\nloadEnvConfig(process.cwd());\n\nimport { defineConfig } from \"drizzle-kit\";\nimport { databaseConfig } from \"./src/lib/db/config\";\n\nexport default defineConfig({\n  schema: \"./src/lib/*/schema.ts\",\n  out: \"./src/lib/db/migrations\",\n  dialect: \"postgresql\",\n  dbCredentials: {\n    url: databaseConfig.server.url,\n  },\n});\n```\n\nThe `loadEnvConfig` call at the top loads environment variables from `.env.development`, `.env.local`, and other `.env` files in the same order as Next.js. This ensures your `DATABASE_URL` is available when running Drizzle Kit commands like `drizzle-kit generate` or `drizzle-kit migrate`.\n\nThe `schema` glob pattern picks up `schema.ts` files from all feature libraries in `src/lib/`, following the \"everything is a library\" pattern where each feature owns its own schema. See [Philosophy](/philosophy) for more details.\n\n### Step 9: Add package.json scripts\n\nAdd these scripts to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"db:generate\": \"drizzle-kit generate\",\n    \"db:migrate\": \"drizzle-kit migrate\",\n    \"db:studio\": \"drizzle-kit studio\"\n  }\n}\n```\n\n### Step 11: Generate and run migrations\n\n```bash\nbun run db:generate\nbun run db:migrate\n```\n\n---\n\n## Understanding Connection Pooling\n\nThe `attachDatabasePool` helper from `@vercel/functions` is the key to efficient database connections on Vercel.\n\n**Why it matters:**\n\n1. **Without pooling**: Each request opens a new TCP connection (~8 roundtrips), adding latency\n2. **With pooling**: The first request establishes a connection; subsequent requests reuse it instantly\n3. **The helper**: `attachDatabasePool` ensures idle connections close gracefully before function suspension, preventing connection leaks\n\n---\n\n## Info: Alternative Drivers\n\nThis recipe uses `node-postgres` (the `pg` package) because it provides the best performance on Vercel with Fluid compute. However, Drizzle supports other Postgres drivers:\n\n| Driver | When to consider |\n| **postgres.js** | If you prefer its API or need specific features like tagged template queries |\n| **Neon Serverless** | For platforms without connection pooling (Netlify, Deno Deploy, Cloudflare Workers) |\n\n> **Note**: If you're deploying to a serverless platform that doesn't support connection pooling, the [Neon Serverless driver](https://orm.drizzle.team/docs/connect-neon) connects over HTTP (~3 roundtrips) instead of TCP (~8 roundtrips), which is faster for single queries in classic serverless environments.\n\n---\n\n## References\n\n- [Neon MCP Server](https://github.com/neondatabase/mcp-server-neon)\n- [Drizzle Postgres docs](https://orm.drizzle.team/docs/get-started-postgresql)\n- [Drizzle Neon integration](https://orm.drizzle.team/docs/connect-neon)\n- [Vercel Connection Pooling Guide](https://vercel.com/guides/connection-pooling-with-functions)\n- [Neon + Vercel Connection Methods](https://neon.tech/docs/guides/vercel-connection-methods)"}