Skip to main content
Drizzle ORM is designed to work seamlessly in edge runtime environments, enabling you to run database queries close to your users anywhere in the world.

What is Edge Runtime?

Edge runtimes are JavaScript environments that run on globally distributed networks (CDNs), providing:
  • Low latency: Code executes geographically close to users
  • Global distribution: Deploy to 200+ locations worldwide
  • Instant cold starts: Sub-50ms initialization times
  • Cost efficiency: Pay only for actual execution time
  • Auto-scaling: Handle millions of requests automatically

Supported Edge Platforms

Drizzle works with all major edge platforms:

Cloudflare Workers

Deploy to 300+ cities with D1 SQLite database

Vercel Edge Functions

Serverless functions with edge caching

Deno Deploy

Edge runtime with native TypeScript support

Netlify Edge Functions

Edge functions powered by Deno

Edge Runtime Constraints

Edge environments have specific limitations you must understand:
No TCP Connections: Traditional database drivers using TCP (like pg, mysql2) don’t work in edge runtimes. Use HTTP-based drivers instead.
Limited APIs: Node.js APIs like fs, net, and child_process are not available. Drizzle’s edge-compatible drivers avoid these dependencies.

Compatible Database Drivers

DatabaseEdge DriverPackage
PostgreSQLNeon HTTPdrizzle-orm/neon-http
PostgreSQLVercel Postgresdrizzle-orm/vercel-postgres
MySQLPlanetScaledrizzle-orm/planetscale-serverless
SQLiteCloudflare D1drizzle-orm/d1
SQLiteTursodrizzle-orm/libsql

Cloudflare Workers

Cloudflare Workers run on one of the world’s largest edge networks with 300+ data centers.

Cloudflare D1 (SQLite)

D1 is Cloudflare’s serverless SQLite database, globally replicated to all edge locations.
1

Install dependencies

npm install drizzle-orm
npm install -D drizzle-kit
D1 types are included in @cloudflare/workers-types
2

Define your schema

schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  name: text('name').notNull(),
  email: text('email').notNull().unique(),
});

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  title: text('title').notNull(),
  content: text('content'),
  userId: integer('user_id').notNull().references(() => users.id),
});
3

Configure Drizzle Kit

drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/schema.ts',
  out: './drizzle',
  dialect: 'sqlite',
  driver: 'd1-http',
  dbCredentials: {
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
    token: process.env.CLOUDFLARE_API_TOKEN!,
  },
});
4

Create your Worker

src/index.ts
import { drizzle } from 'drizzle-orm/d1';
import { users, posts } from './schema';

export interface Env {
  DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const db = drizzle(env.DB);
    
    // Query the database
    const allUsers = await db.select().from(users);
    
    return Response.json(allUsers);
  },
};
5

Configure wrangler.toml

wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"
6

Generate and apply migrations

# Generate migration files
npx drizzle-kit generate

# Apply migrations to D1
npx wrangler d1 migrations apply my-database

D1 Batch Operations

D1 supports efficient batch queries to reduce latency:
import { drizzle } from 'drizzle-orm/d1';

const db = drizzle(env.DB);

// Execute multiple queries in one round trip
const [usersResult, postsResult] = await db.batch([
  db.select().from(users),
  db.select().from(posts).limit(10),
]);

D1 Transactions

await db.transaction(async (tx) => {
  const user = await tx.insert(users)
    .values({ name: 'John', email: '[email protected]' })
    .returning();
  
  await tx.insert(posts).values({
    title: 'First Post',
    userId: user[0].id,
  });
});

Connecting to External Databases

For PostgreSQL or MySQL, use HTTP-based drivers:
import { drizzle } from 'drizzle-orm/neon-http';

export interface Env {
  DATABASE_URL: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const db = drizzle({ connection: env.DATABASE_URL });
    
    const users = await db.select().from(usersTable);
    return Response.json(users);
  },
};

Vercel Edge Functions

Vercel Edge Functions run on the Vercel Edge Network using the V8 runtime.

Setup with Vercel Postgres

1

Create Vercel Postgres database

In your Vercel project dashboard:
  1. Go to Storage tab
  2. Create new Postgres database
  3. Environment variables are auto-injected
2

Install dependencies

npm install drizzle-orm @vercel/postgres
npm install -D drizzle-kit
3

Create edge API route

app/api/users/route.ts
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { pgTable, serial, text } from 'drizzle-orm/pg-core';

export const runtime = 'edge';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export async function GET() {
  const db = drizzle();
  const allUsers = await db.select().from(users);
  
  return Response.json(allUsers);
}

export async function POST(request: Request) {
  const db = drizzle();
  const body = await request.json();
  
  const newUser = await db.insert(users)
    .values({ name: body.name })
    .returning();
  
  return Response.json(newUser[0]);
}

Edge Middleware Example

middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { drizzle } from 'drizzle-orm/vercel-postgres';
import { users } from './schema';
import { eq } from 'drizzle-orm';

export const config = {
  matcher: '/api/:path*',
};

export async function middleware(request: NextRequest) {
  const apiKey = request.headers.get('x-api-key');
  
  if (!apiKey) {
    return NextResponse.json(
      { error: 'API key required' },
      { status: 401 }
    );
  }
  
  // Validate API key against database
  const db = drizzle();
  const user = await db.select()
    .from(users)
    .where(eq(users.apiKey, apiKey))
    .limit(1);
  
  if (user.length === 0) {
    return NextResponse.json(
      { error: 'Invalid API key' },
      { status: 401 }
    );
  }
  
  // Add user info to request headers
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-user-id', user[0].id.toString());
  
  return NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  });
}

Using with Neon

app/api/posts/route.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { posts } from '@/schema';

export const runtime = 'edge';

export async function GET() {
  const db = drizzle({ 
    connection: process.env.DATABASE_URL! 
  });
  
  const allPosts = await db.select().from(posts);
  return Response.json(allPosts);
}

Deno Deploy

Deno Deploy is a globally distributed edge runtime with native TypeScript support.

PostgreSQL with Neon

main.ts
import { drizzle } from 'npm:drizzle-orm/neon-http';
import { neon } from 'npm:@neondatabase/serverless';
import { pgTable, serial, text } from 'npm:drizzle-orm/pg-core';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

const sql = neon(Deno.env.get('DATABASE_URL')!);
const db = drizzle(sql);

Deno.serve(async (req) => {
  const url = new URL(req.url);
  
  if (url.pathname === '/users') {
    const allUsers = await db.select().from(users);
    return Response.json(allUsers);
  }
  
  return new Response('Not found', { status: 404 });
});

SQLite with Turso

import { drizzle } from 'npm:drizzle-orm/libsql';
import { createClient } from 'npm:@libsql/client';

const client = createClient({
  url: Deno.env.get('TURSO_DATABASE_URL')!,
  authToken: Deno.env.get('TURSO_AUTH_TOKEN')!,
});

const db = drizzle(client);

Deno.serve(async () => {
  const users = await db.select().from(usersTable);
  return Response.json(users);
});

Netlify Edge Functions

Netlify Edge Functions use Deno runtime and work similarly to Deno Deploy.
netlify/edge-functions/users.ts
import { drizzle } from 'npm:drizzle-orm/neon-http';
import { users } from '../../schema.ts';

export default async () => {
  const db = drizzle({ 
    connection: Deno.env.get('DATABASE_URL')! 
  });
  
  const allUsers = await db.select().from(users);
  
  return Response.json(allUsers);
};

export const config = { path: '/api/users' };

Performance Optimization

Minimize Round Trips

Use batch queries when possible:
// Bad: 3 round trips
const user = await db.select().from(users).where(eq(users.id, 1));
const posts = await db.select().from(postsTable).where(eq(postsTable.userId, 1));
const comments = await db.select().from(commentsTable).limit(10);

// Good: 1 round trip
const [user, posts, comments] = await db.batch([
  db.select().from(users).where(eq(users.id, 1)),
  db.select().from(postsTable).where(eq(postsTable.userId, 1)),
  db.select().from(commentsTable).limit(10),
]);

Use Relational Queries

Drizzle’s relational queries can fetch nested data efficiently:
import { relations } from 'drizzle-orm';

export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

// Single query with joins
const usersWithPosts = await db.query.users.findMany({
  with: {
    posts: true,
  },
});

Cache Aggressively

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const cache = caches.default;
    
    // Try cache first
    let response = await cache.match(request);
    if (response) return response;
    
    // Query database
    const db = drizzle(env.DB);
    const users = await db.select().from(usersTable);
    
    response = Response.json(users);
    
    // Cache for 60 seconds
    response.headers.set('Cache-Control', 's-maxage=60');
    await cache.put(request, response.clone());
    
    return response;
  },
};

Connection Initialization

Initialize database connections outside request handlers when possible:
// Good: Initialize once
import { drizzle } from 'drizzle-orm/d1';

let db: ReturnType<typeof drizzle> | null = null;

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (!db) {
      db = drizzle(env.DB);
    }
    
    const users = await db.select().from(usersTable);
    return Response.json(users);
  },
};

Error Handling

Implement proper error handling for network issues:
import { drizzle } from 'drizzle-orm/neon-http';

export async function GET() {
  try {
    const db = drizzle({ connection: process.env.DATABASE_URL! });
    const users = await db.select().from(usersTable);
    
    return Response.json(users);
  } catch (error) {
    console.error('Database error:', error);
    
    // Return appropriate error response
    if (error instanceof Error && error.message.includes('timeout')) {
      return Response.json(
        { error: 'Database timeout' },
        { status: 504 }
      );
    }
    
    return Response.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

TypeScript Configuration

Ensure your TypeScript config works with edge runtimes:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": ["@cloudflare/workers-types"]
  }
}

Deployment Checklist

1

Verify edge compatibility

  • Use HTTP-based database drivers only
  • Avoid Node.js-specific APIs
  • Test locally with edge runtime emulators
2

Optimize bundle size

  • Import only needed Drizzle modules
  • Use tree-shaking friendly imports
  • Check bundle size with your platform’s CLI
3

Configure environment variables

  • Set DATABASE_URL in platform dashboard
  • Use platform-specific secrets management
  • Never commit credentials to git
4

Test in staging

  • Deploy to preview/staging environment first
  • Verify queries work correctly
  • Check latency and performance
5

Monitor production

  • Set up error tracking (Sentry, LogRocket, etc.)
  • Monitor query performance
  • Track edge function execution time

Troubleshooting

You’re using a Node.js-specific driver. Switch to an edge-compatible driver:
  • pgdrizzle-orm/neon-http
  • mysql2drizzle-orm/planetscale-serverless
  • better-sqlite3drizzle-orm/d1 or drizzle-orm/libsql
Edge functions have strict execution time limits (usually 10-30 seconds):
  • Optimize slow queries with indexes
  • Use batch operations to reduce round trips
  • Consider caching frequently accessed data
Reduce bundle size:
  • Import specific Drizzle modules: import { eq } from 'drizzle-orm'
  • Avoid importing entire schema in edge functions
  • Use platform-specific bundling optimizations
Verify:
  • Environment variables are set correctly
  • Connection string includes proper authentication
  • Database allows connections from edge IP ranges
  • SSL/TLS is configured if required

Next Steps

Serverless Databases

Learn about serverless database platforms

Best Practices

Optimize your Drizzle implementation

Schema Design

Design efficient database schemas

Query Performance

Write fast, optimized queries

Build docs developers (and LLMs) love