479 lines
11 KiB
Markdown
479 lines
11 KiB
Markdown
# Adapter Reference Guide
|
|
|
|
Complete guide for choosing between HTTP and WebSocket adapters.
|
|
|
|
## Table of Contents
|
|
|
|
- [Quick Decision Matrix](#quick-decision-matrix)
|
|
- [HTTP Adapter](#http-adapter-neondatabaseserverless-with-neon-http)
|
|
- [WebSocket Adapter](#websocket-adapter-neondatabaseserverless-with-neon-serverless)
|
|
- [Framework-Specific Recommendations](#framework-specific-recommendations)
|
|
- [Mixed Environments](#mixed-environments)
|
|
- [Feature Comparison Table](#feature-comparison-table)
|
|
- [Performance Considerations](#performance-considerations)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Migration Between Adapters](#migration-between-adapters)
|
|
- [Choosing the Right Adapter](#choosing-the-right-adapter)
|
|
- [Related Resources](#related-resources)
|
|
|
|
---
|
|
|
|
## Quick Decision Matrix
|
|
|
|
| Environment | Adapter | Reason |
|
|
|-------------|---------|--------|
|
|
| Vercel | HTTP | Edge functions, stateless |
|
|
| Cloudflare Workers | HTTP | Edge runtime, no WebSocket |
|
|
| AWS Lambda | HTTP | Stateless, cold starts |
|
|
| Next.js (Vercel) | HTTP | App Router, Edge Runtime |
|
|
| Express/Fastify | WebSocket | Long-lived connections |
|
|
| Node.js server | WebSocket | Connection pooling |
|
|
| Bun server | WebSocket | Persistent runtime |
|
|
|
|
## HTTP Adapter (@neondatabase/serverless with neon-http)
|
|
|
|
### When to Use
|
|
|
|
✅ **Serverless/Edge environments:**
|
|
- Vercel Edge Functions
|
|
- Cloudflare Workers
|
|
- AWS Lambda
|
|
- Deno Deploy
|
|
- Next.js App Router (default)
|
|
|
|
✅ **Characteristics:**
|
|
- Stateless requests
|
|
- Cold starts
|
|
- Short execution time
|
|
- No persistent connections
|
|
|
|
### Setup
|
|
|
|
**Installation:**
|
|
```bash
|
|
npm add drizzle-orm @neondatabase/serverless
|
|
npm add -D drizzle-kit
|
|
```
|
|
|
|
**Connection:**
|
|
```typescript
|
|
import { drizzle } from 'drizzle-orm/neon-http';
|
|
import { neon } from '@neondatabase/serverless';
|
|
|
|
const sql = neon(process.env.DATABASE_URL!);
|
|
export const db = drizzle(sql);
|
|
```
|
|
|
|
**Complete example:** See `templates/db-http.ts`
|
|
|
|
### Pros
|
|
|
|
✅ **Perfect for serverless:**
|
|
- No connection management needed
|
|
- Works in edge environments
|
|
- Fast cold starts
|
|
- Auto-scales
|
|
|
|
✅ **Simple:**
|
|
- Minimal configuration
|
|
- No connection pooling complexity
|
|
- Stateless = predictable
|
|
|
|
### Cons
|
|
|
|
❌ **Limited features:**
|
|
- No transactions
|
|
- No prepared statements
|
|
- No streaming
|
|
- Higher latency per query
|
|
|
|
❌ **Not ideal for:**
|
|
- Batch operations
|
|
- Complex transactions
|
|
- High-frequency queries from same process
|
|
|
|
### Best Practices
|
|
|
|
**1. Use batch for multiple operations:**
|
|
```typescript
|
|
await db.batch([
|
|
db.insert(users).values({ email: 'test@example.com' }),
|
|
db.insert(posts).values({ title: 'Test' }),
|
|
]);
|
|
```
|
|
|
|
**2. Cache query results:**
|
|
```typescript
|
|
import { unstable_cache } from 'next/cache';
|
|
|
|
const getUsers = unstable_cache(
|
|
async () => db.select().from(users),
|
|
['users'],
|
|
{ revalidate: 60 }
|
|
);
|
|
```
|
|
|
|
**3. Minimize round trips:**
|
|
```typescript
|
|
const usersWithPosts = await db.query.users.findMany({
|
|
with: { posts: true },
|
|
});
|
|
```
|
|
|
|
## WebSocket Adapter (@neondatabase/serverless with neon-serverless)
|
|
|
|
### When to Use
|
|
|
|
✅ **Long-lived processes:**
|
|
- Express/Fastify servers
|
|
- Standard Node.js applications
|
|
- Background workers
|
|
- WebSocket servers
|
|
- Bun applications
|
|
|
|
✅ **Characteristics:**
|
|
- Persistent connections
|
|
- Long execution time
|
|
- Connection pooling
|
|
- Complex transactions
|
|
|
|
### Setup
|
|
|
|
**Installation:**
|
|
```bash
|
|
npm add drizzle-orm @neondatabase/serverless ws
|
|
npm add -D drizzle-kit @types/ws
|
|
```
|
|
|
|
**Connection:**
|
|
```typescript
|
|
import { drizzle } from 'drizzle-orm/neon-serverless';
|
|
import { Pool, neonConfig } from '@neondatabase/serverless';
|
|
import ws from 'ws';
|
|
|
|
neonConfig.webSocketConstructor = ws;
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL!,
|
|
max: 10,
|
|
idleTimeoutMillis: 30000,
|
|
connectionTimeoutMillis: 5000,
|
|
});
|
|
|
|
export const db = drizzle(pool);
|
|
```
|
|
|
|
**Complete example:** See `templates/db-websocket.ts`
|
|
|
|
### Pros
|
|
|
|
✅ **Full features:**
|
|
- Transactions
|
|
- Prepared statements
|
|
- Streaming
|
|
- Lower latency (persistent connection)
|
|
|
|
✅ **Better for:**
|
|
- Multiple queries per request
|
|
- Complex business logic
|
|
- High-frequency operations
|
|
|
|
### Cons
|
|
|
|
❌ **More complex:**
|
|
- Connection pool management
|
|
- Need to handle connection errors
|
|
- Not available in edge environments
|
|
|
|
❌ **Resource considerations:**
|
|
- Connection limits
|
|
- Memory usage
|
|
- Cold start overhead
|
|
|
|
### Best Practices
|
|
|
|
**1. Configure connection pool:**
|
|
```typescript
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL!,
|
|
max: 10, // Max connections
|
|
idleTimeoutMillis: 30000, // Close idle after 30s
|
|
connectionTimeoutMillis: 5000, // Timeout after 5s
|
|
});
|
|
```
|
|
|
|
**2. Graceful shutdown:**
|
|
```typescript
|
|
process.on('SIGTERM', async () => {
|
|
await pool.end();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on('SIGINT', async () => {
|
|
await pool.end();
|
|
process.exit(0);
|
|
});
|
|
```
|
|
|
|
**3. Use transactions:**
|
|
```typescript
|
|
await db.transaction(async (tx) => {
|
|
const user = await tx.insert(users)
|
|
.values({ email: 'test@example.com' })
|
|
.returning();
|
|
|
|
await tx.insert(posts)
|
|
.values({ userId: user[0].id, title: 'First post' });
|
|
});
|
|
```
|
|
|
|
**4. Handle connection errors:**
|
|
```typescript
|
|
pool.on('error', (err) => {
|
|
console.error('Unexpected pool error:', err);
|
|
});
|
|
|
|
pool.on('connect', () => {
|
|
console.log('Pool connection established');
|
|
});
|
|
```
|
|
|
|
## Framework-Specific Recommendations
|
|
|
|
### Next.js
|
|
|
|
**App Router (default):**
|
|
- Use HTTP adapter (Edge Runtime)
|
|
- Server Actions → HTTP
|
|
- Route Handlers → HTTP
|
|
|
|
**Pages Router:**
|
|
- API Routes → Either adapter works
|
|
- Recommend HTTP for consistency
|
|
|
|
**Example:**
|
|
```typescript
|
|
// app/actions/users.ts
|
|
'use server';
|
|
|
|
import { db } from '@/db'; // HTTP adapter
|
|
import { users } from '@/db/schema';
|
|
|
|
export async function createUser(email: string) {
|
|
return db.insert(users).values({ email }).returning();
|
|
}
|
|
```
|
|
|
|
### Express
|
|
|
|
**Standard setup:**
|
|
- Use WebSocket adapter
|
|
- Configure connection pool
|
|
- Implement health checks
|
|
|
|
**Example:**
|
|
```typescript
|
|
import express from 'express';
|
|
import { db } from './db'; // WebSocket adapter
|
|
import { users } from './db/schema';
|
|
|
|
const app = express();
|
|
|
|
app.get('/health', async (req, res) => {
|
|
try {
|
|
await db.select().from(users).limit(1);
|
|
res.json({ status: 'healthy' });
|
|
} catch (err) {
|
|
res.status(500).json({ status: 'unhealthy', error: err.message });
|
|
}
|
|
});
|
|
|
|
app.listen(3000);
|
|
```
|
|
|
|
### Vite/React (SPA)
|
|
|
|
**Deployment matters:**
|
|
|
|
**If deploying to Vercel:**
|
|
- API routes → HTTP adapter
|
|
- Static files → No backend needed
|
|
|
|
**If deploying to Node.js server:**
|
|
- Backend API → WebSocket adapter
|
|
- Frontend → Fetch from API
|
|
|
|
### Bun
|
|
|
|
**Recommendation:**
|
|
- Use WebSocket adapter
|
|
- Bun has built-in WebSocket support
|
|
- No need for `ws` package
|
|
|
|
**Setup:**
|
|
```typescript
|
|
import { drizzle } from 'drizzle-orm/neon-serverless';
|
|
import { Pool } from '@neondatabase/serverless';
|
|
|
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
|
|
export const db = drizzle(pool);
|
|
```
|
|
|
|
## Mixed Environments
|
|
|
|
### Using Both Adapters
|
|
|
|
If you have both serverless and long-lived components:
|
|
|
|
**Structure:**
|
|
```
|
|
src/
|
|
├── db/
|
|
│ ├── http.ts # HTTP adapter for serverless
|
|
│ ├── ws.ts # WebSocket for servers
|
|
│ └── schema.ts # Shared schema
|
|
```
|
|
|
|
**HTTP adapter:**
|
|
```typescript
|
|
// src/db/http.ts
|
|
import { drizzle } from 'drizzle-orm/neon-http';
|
|
import { neon } from '@neondatabase/serverless';
|
|
|
|
const sql = neon(process.env.DATABASE_URL!);
|
|
export const httpDb = drizzle(sql);
|
|
```
|
|
|
|
**WebSocket adapter:**
|
|
```typescript
|
|
// src/db/ws.ts
|
|
import { drizzle } from 'drizzle-orm/neon-serverless';
|
|
import { Pool, neonConfig } from '@neondatabase/serverless';
|
|
import ws from 'ws';
|
|
|
|
neonConfig.webSocketConstructor = ws;
|
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
|
|
export const wsDb = drizzle(pool);
|
|
```
|
|
|
|
**Usage:**
|
|
```typescript
|
|
// Vercel Edge Function
|
|
import { httpDb as db } from '@/db/http';
|
|
|
|
// Express route
|
|
import { wsDb as db } from '@/db/ws';
|
|
```
|
|
|
|
## Feature Comparison Table
|
|
|
|
| Feature | HTTP Adapter | WebSocket Adapter |
|
|
|---------|-------------|-------------------|
|
|
| Transactions | ❌ No | ✅ Yes |
|
|
| Prepared statements | ❌ No | ✅ Yes |
|
|
| Streaming results | ❌ No | ✅ Yes |
|
|
| Connection pooling | N/A (stateless) | ✅ Yes |
|
|
| Edge runtime | ✅ Yes | ❌ No |
|
|
| Cold start speed | ✅ Fast | ⚠️ Slower |
|
|
| Latency per query | ⚠️ Higher | ✅ Lower |
|
|
| Batch operations | ✅ Yes | ✅ Yes |
|
|
| Max connection limit | N/A | ⚠️ Applies |
|
|
|
|
## Performance Considerations
|
|
|
|
### HTTP Adapter Performance
|
|
|
|
**Optimize by:**
|
|
- Minimizing round trips
|
|
- Using batch operations
|
|
- Caching query results
|
|
- Pre-fetching related data
|
|
|
|
**Typical latency:**
|
|
- Single query: 50-200ms
|
|
- Batch operation: 100-300ms
|
|
|
|
### WebSocket Adapter Performance
|
|
|
|
**Optimize by:**
|
|
- Configuring pool size correctly
|
|
- Using transactions for related operations
|
|
- Implementing query caching
|
|
- Monitoring connection usage
|
|
|
|
**Typical latency:**
|
|
- First query (connection): 50-100ms
|
|
- Subsequent queries: 10-50ms
|
|
|
|
## Troubleshooting
|
|
|
|
### HTTP Adapter Issues
|
|
|
|
**Problem:** "fetch is not defined"
|
|
- **Solution:** Ensure running in environment with fetch API (Node 18+, edge runtime)
|
|
|
|
**Problem:** Slow queries
|
|
- **Solution:** Use batch operations, reduce round trips
|
|
|
|
### WebSocket Adapter Issues
|
|
|
|
**Problem:** "WebSocket is not defined"
|
|
- **Solution:** Add `neonConfig.webSocketConstructor = ws`
|
|
|
|
**Problem:** "Too many connections"
|
|
- **Solution:** Reduce pool `max` size, ensure connections are closed
|
|
|
|
**Problem:** Connection timeouts
|
|
- **Solution:** Increase `connectionTimeoutMillis`, implement retry logic
|
|
|
|
## Migration Between Adapters
|
|
|
|
### HTTP → WebSocket
|
|
|
|
**When:** Moving from serverless to dedicated server.
|
|
|
|
**Steps:**
|
|
1. Install ws: `npm add ws @types/ws`
|
|
2. Update connection file to WebSocket adapter
|
|
3. Update drizzle.config.ts if needed
|
|
4. Test transactions (now available)
|
|
|
|
### WebSocket → HTTP
|
|
|
|
**When:** Moving to serverless/edge deployment.
|
|
|
|
**Steps:**
|
|
1. Update connection file to HTTP adapter
|
|
2. Remove ws dependency
|
|
3. **Important:** Replace transactions with batch operations
|
|
4. Test thoroughly (feature differences)
|
|
|
|
## Choosing the Right Adapter
|
|
|
|
**Ask yourself:**
|
|
|
|
1. **Where am I deploying?**
|
|
- Edge/Serverless → HTTP
|
|
- Node.js server → WebSocket
|
|
|
|
2. **Do I need transactions?**
|
|
- Yes → WebSocket
|
|
- No → Either works
|
|
|
|
3. **What's my request pattern?**
|
|
- Short, infrequent → HTTP
|
|
- Long, frequent → WebSocket
|
|
|
|
4. **Am I optimizing for?**
|
|
- Cold starts → HTTP
|
|
- Latency → WebSocket
|
|
|
|
**When in doubt:** Start with HTTP (simpler), migrate to WebSocket if needed.
|
|
|
|
## Related Resources
|
|
|
|
- `guides/new-project.md` - Setup guides for both adapters
|
|
- `guides/troubleshooting.md` - Connection error solutions
|
|
- `templates/db-http.ts` - HTTP adapter template
|
|
- `templates/db-websocket.ts` - WebSocket adapter template
|