Neon and Drizzle ORM are the most popular combination for Next.js applications that need a real Postgres database. Drizzle is TypeScript-native, generates zero-overhead queries, and integrates with Neon's HTTP driver — which is essential for Edge Runtime and Vercel Functions where the standard pg TCP driver doesn't work.

Why the HTTP Driver Matters

Standard Postgres drivers (pg, postgres.js) use TCP connections. Vercel Edge Functions and Cloudflare Workers don't support TCP — they only allow HTTP/WebSocket connections. Neon's @neondatabase/serverless package provides an HTTP-based driver that works everywhere.

Driver Works in Edge Runtime Works in Node.js Connection pooling
pg (standard) No Yes Manual
@neondatabase/serverless (HTTP) Yes Yes Built-in
@neondatabase/serverless (WebSocket) Yes Yes Built-in

Project Setup

npm install drizzle-orm @neondatabase/serverless
npm install -D drizzle-kit tsx
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
 
export default defineConfig({
  schema: './lib/db/schema.ts',
  out: './drizzle',
  dialect: 'postgresql',
  dbCredentials: {
    // Use direct URL for migrations
    url: process.env.DATABASE_URL_DIRECT!,
  },
});

Schema Definition

// lib/db/schema.ts
import {
  pgTable, uuid, text, timestamp,
  integer, vector, boolean
} from 'drizzle-orm/pg-core';
 
export const users = pgTable('users', {
  id:        uuid('id').primaryKey().defaultRandom(),
  email:     text('email').unique().notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});
 
export const documents = pgTable('documents', {
  id:        uuid('id').primaryKey().defaultRandom(),
  userId:    uuid('user_id').references(() => users.id, { onDelete: 'cascade' }),
  title:     text('title').notNull(),
  content:   text('content').notNull(),
  processed: boolean('processed').default(false),
  createdAt: timestamp('created_at').defaultNow(),
});
 
export const chunks = pgTable('chunks', {
  id:         uuid('id').primaryKey().defaultRandom(),
  documentId: uuid('document_id').references(() => documents.id, { onDelete: 'cascade' }),
  chunkText:  text('chunk_text').notNull(),
  embedding:  vector('embedding', { dimensions: 1536 }),
  chunkIndex: integer('chunk_index').notNull(),
});

Database Client Setup

// lib/db/index.ts
import { neon } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
import * as schema from './schema';
 
// Singleton for serverless — create once per cold start
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
 
// Type helpers
export type Document = typeof schema.documents.$inferSelect;
export type NewDocument = typeof schema.documents.$inferInsert;

Common Query Patterns

import { db } from '@/lib/db';
import { documents, chunks } from '@/lib/db/schema';
import { eq, desc, and, sql } from 'drizzle-orm';
 
// Select with filter
const userDocs = await db
  .select()
  .from(documents)
  .where(eq(documents.userId, userId))
  .orderBy(desc(documents.createdAt))
  .limit(20);
 
// Insert and return
const [doc] = await db
  .insert(documents)
  .values({ userId, title, content })
  .returning();
 
// Update
await db
  .update(documents)
  .set({ processed: true })
  .where(eq(documents.id, docId));
 
// Vector similarity search (raw SQL for pgvector)
const similar = await db.execute(sql`
  SELECT id, chunk_text, 1 - (embedding <=> ${queryEmbedding}::vector) AS similarity
  FROM chunks
  ORDER BY embedding <=> ${queryEmbedding}::vector
  LIMIT 5
`);

Running Migrations

# Generate migration from schema changes
npx drizzle-kit generate
 
# Apply to database (uses DATABASE_URL_DIRECT)
npx drizzle-kit migrate
 
# For CI: apply migrations before running tests
DATABASE_URL_DIRECT=$NEON_BRANCH_URL npx drizzle-kit migrate
Metadata Value
Title Neon + Drizzle ORM: The Complete Setup for Next.js and Serverless Environments
Tool Neon
Primary SEO keyword neon drizzle orm next.js
Secondary keywords neon serverless driver, drizzle neon setup, neon edge runtime, neon drizzle migration
Estimated read time 9 minutes
Research date 2026-04-14