SKILL.md
Neon Serverless Postgres
When to Use This Skill
- Setting up Neon database connections
- Using database branching for development
- Configuring connection pooling
- Optimizing for serverless environments
- Integrating Neon with frameworks
Quick Start
Create Project
# Using Neon CLI
npm install -g neonctl
neonctl auth
# Create project
neonctl projects create --name my-project
# Get connection string
neonctl connection-string
Connection String Format
postgresql://[user]:[password]@[endpoint].neon.tech/[database]?sslmode=require
Connection Patterns
Serverless Function (Recommended)
// Use Neon serverless driver for edge/serverless
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
export async function getUsers() {
const users = await sql`SELECT * FROM users LIMIT 10`;
return users;
}
Connection Pooling with Drizzle
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);
// Usage
const users = await db.select().from(usersTable).limit(10);
Node.js with pg (Long-running)
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: true },
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
});
// Use pooled connections
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users');
return result.rows;
} finally {
client.release();
}
Prisma Integration
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL") // For migrations
}
// For edge/serverless
import { PrismaClient } from '@prisma/client'
import { PrismaNeon } from '@prisma/adapter-neon'
import { neon } from '@neondatabase/serverless'
const sql = neon(process.env.DATABASE_URL!)
const adapter = new PrismaNeon(sql)
const prisma = new PrismaClient({ adapter })
Database Branching
Create Development Branch
# Create branch from main
neonctl branches create --name dev --parent main
# Get branch connection string
neonctl connection-string --branch dev
# List branches
neonctl branches list
# Delete branch
neonctl branches delete dev
Branch Workflow
## Development Workflow
1. **Main Branch**: Production data
2. **Dev Branch**: Daily development
3. **Feature Branches**: Per-feature isolation
main (production) ├── dev (shared development) │ ├── feature/auth │ └── feature/payments └── staging (pre-production)
### CI/CD Branch Strategy
.github/workflows/preview.yml
name: Preview Environment on: pull_request: types: [opened, synchronize]
jobs: create-preview: runs-on: ubuntu-latest steps: - name: Create Neon Branch uses: neondatabase/create-branch-action@v4 with: projectid: ${{ secrets.NEONPROJECTID }} branchname: preview-${{ github.event.pullrequest.number }} apikey: ${{ secrets.NEONAPIKEY }}
- name: Run Migrations run: npx prisma migrate deploy env: DATABASEURL: ${{ steps.create-preview.outputs.dburl }}
## Autoscaling Configuration
### Compute Settings
// Neon autoscales based on load // Configure min/max compute units in dashboard
// For predictable workloads, set minimum compute // Project Settings > Compute > Minimum compute size
// Scale to zero saves costs during inactivity // Re-activation takes ~500ms (cold start)
### Handle Cold Starts
// Warm connection on app start async function warmDatabase() { const sql = neon(process.env.DATABASE_URL!); await sqlSELECT 1; }
// Call during initialization warmDatabase().catch(console.error);
## Best Practices
### Query Optimization
-- Use EXPLAIN ANALYZE EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
-- Add appropriate indexes CREATE INDEX idxusersemail ON users(email);
-- Use partial indexes for common filters CREATE INDEX idxactiveusers ON users(created_at) WHERE status = 'active';
### Connection Management
// DON'T: Create new connection per request async function handleRequest() { const pool = new Pool({ connectionString: url }); // BAD const result = await pool.query('SELECT 1'); await pool.end(); }
// DO: Reuse connection pool const pool = new Pool({ connectionString: url }); // Create once
async function handleRequest() { const result = await pool.query('SELECT 1'); return result; }
### Serverless Considerations
// For serverless: Use HTTP-based driver import { neon } from '@neondatabase/serverless'; const sql = neon(process.env.DATABASE_URL!);
// For long-running: Use WebSocket for better performance import { neonConfig, Pool } from '@neondatabase/serverless'; import ws from 'ws';
neonConfig.webSocketConstructor = ws; const pool = new Pool({ connectionString: url });
## Environment Setup
### Next.js
.env.local
DATABASE_URL="postgresql://user:[email protected]/dbname?sslmode=require"
For Prisma migrations (bypasses pooler)
DIRECT_URL="postgresql://user:[email protected]/dbname?sslmode=require"
### Vercel Integration
Connect Neon to Vercel
1. Go to Neon Dashboard > Integrations
2. Select Vercel
3. Authorize and select project
Environment variables are set automatically
## Monitoring
### Built-in Metrics
Available in Neon Console
- Connection count
- Query performance
- Storage usage
- Compute hours
- Data transfer
### Query Performance
-- Enable query statistics CREATE EXTENSION IF NOT EXISTS pgstatstatements;
-- Find slow queries SELECT query, calls, meanexectime, totalexectime FROM pgstatstatements ORDER BY totalexectime DESC LIMIT 10;
## Schema Management
### Migrations with Prisma
Development
npx prisma migrate dev --name add_users
Production
npx prisma migrate deploy
### Migrations with Drizzle
Generate migration
npx drizzle-kit generate:pg
Apply migration
npx drizzle-kit push:pg
## Checklist
- [ ] Use serverless driver for edge functions
- [ ] Configure connection pooling appropriately
- [ ] Set up database branching for dev/staging
- [ ] Add indexes for common queries
- [ ] Monitor query performance
- [ ] Use SSL connections (sslmode=require)
- [ ] Configure autoscaling limits
- [ ] Handle cold starts gracefully