Vibeship-spawner-skills neon-postgres

Neon Serverless Postgres Skill

install
source · Clone the upstream repo
git clone https://github.com/vibeforge1111/vibeship-spawner-skills
manifest: integrations/neon-postgres/skill.yaml
source content

Neon Serverless Postgres Skill

Patterns for serverless database, branching, and connection management

id: neon-postgres name: Neon Postgres display_name: Neon Serverless Postgres description: Expert patterns for Neon serverless Postgres, branching, connection pooling, and Prisma/Drizzle integration version: 1.0.0 category: integrations tags:

  • neon
  • postgres
  • serverless
  • database
  • branching
  • prisma
  • drizzle
  • connection-pooling

triggers:

  • "neon database"
  • "serverless postgres"
  • "database branching"
  • "neon postgres"
  • "postgres serverless"
  • "connection pooling"
  • "preview environments"
  • "database per preview"

capabilities:

  • "Prisma and Drizzle ORM integration"
  • "Connection pooling with PgBouncer"
  • "Database branching for development"
  • "Vercel preview environment integration"
  • "Autoscaling and scale-to-zero"
  • "Serverless driver (HTTP/WebSocket)"
  • "Schema migrations with branches"

patterns:

  • id: prisma-connection-setup name: Prisma with Neon Connection description: | Configure Prisma for Neon with connection pooling.

    Use two connection strings:

    • DATABASE_URL: Pooled connection for Prisma Client
    • DIRECT_URL: Direct connection for Prisma Migrate

    The pooled connection uses PgBouncer for up to 10K connections. Direct connection required for migrations (DDL operations).

    code_example: |

    .env

    Pooled connection for application queries

    DATABASE_URL="postgres://user:password@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require"

    Direct connection for migrations

    DIRECT_URL="postgres://user:password@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require"

    // prisma/schema.prisma generator client { provider = "prisma-client-js" }

    datasource db { provider = "postgresql" url = env("DATABASE_URL") directUrl = env("DIRECT_URL") }

    model User { id String @id @default(cuid()) email String @unique name String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }

    // lib/prisma.ts import { PrismaClient } from '@prisma/client';

    const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined; };

    export const prisma = globalForPrisma.prisma ?? new PrismaClient({ log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], });

    if (process.env.NODE_ENV !== 'production') { globalForPrisma.prisma = prisma; }

    // Run migrations // Uses DIRECT_URL automatically npx prisma migrate dev npx prisma migrate deploy

    anti_patterns:

    • pattern: "Using pooled connection for migrations" why: "DDL operations fail through PgBouncer" fix: "Set directUrl in schema.prisma"

    • pattern: "Not using connection pooling" why: "Serverless functions exhaust connection limits" fix: "Use -pooler endpoint in DATABASE_URL"

    references:

  • id: drizzle-serverless-driver name: Drizzle with Neon Serverless Driver description: | Use Drizzle ORM with Neon's serverless HTTP driver for edge/serverless environments.

    Two driver options:

    • neon-http: Single queries over HTTP (fastest for one-off queries)
    • neon-serverless: WebSocket for transactions and sessions

    code_example: |

    Install dependencies

    npm install drizzle-orm @neondatabase/serverless npm install -D drizzle-kit

    // lib/db/schema.ts import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';

    export const users = pgTable('users', { id: serial('id').primaryKey(), email: text('email').notNull().unique(), name: text('name'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), });

    // lib/db/index.ts (for serverless - HTTP driver) import { neon } from '@neondatabase/serverless'; import { drizzle } from 'drizzle-orm/neon-http'; import * as schema from './schema';

    const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql, { schema });

    // Usage in API route import { db } from '@/lib/db'; import { users } from '@/lib/db/schema';

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

    // lib/db/index.ts (for WebSocket - transactions) import { Pool } from '@neondatabase/serverless'; import { drizzle } from 'drizzle-orm/neon-serverless'; import * as schema from './schema';

    const pool = new Pool({ connectionString: process.env.DATABASE_URL }); export const db = drizzle(pool, { schema });

    // With transactions await db.transaction(async (tx) => { await tx.insert(users).values({ email: 'test@example.com' }); await tx.update(users).set({ name: 'Updated' }); });

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

    export default defineConfig({ schema: './lib/db/schema.ts', out: './drizzle', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL!, }, });

    // Run migrations npx drizzle-kit generate npx drizzle-kit migrate

    anti_patterns:

    • pattern: "Using pg driver in serverless" why: "TCP connections don't work in all edge environments" fix: "Use @neondatabase/serverless driver"

    • pattern: "HTTP driver for transactions" why: "HTTP driver doesn't support transactions" fix: "Use WebSocket driver (Pool) for transactions"

    references:

  • id: connection-pooling name: Connection Pooling with PgBouncer description: | Neon provides built-in connection pooling via PgBouncer.

    Key limits:

    • Up to 10,000 concurrent connections to pooler
    • Connections still consume underlying Postgres connections
    • 7 connections reserved for Neon superuser

    Use pooled endpoint for application, direct for migrations.

    code_example: |

    Connection string formats

    Pooled connection (for application)

    Note: -pooler in hostname

    postgres://user:pass@ep-cool-name-pooler.us-east-2.aws.neon.tech/neondb

    Direct connection (for migrations)

    Note: No -pooler

    postgres://user:pass@ep-cool-name.us-east-2.aws.neon.tech/neondb

    // Prisma with pooling // prisma/schema.prisma datasource db { provider = "postgresql" url = env("DATABASE_URL") // Pooled directUrl = env("DIRECT_URL") // Direct }

    // Connection pool settings for high-traffic // lib/prisma.ts import { PrismaClient } from '@prisma/client';

    export const prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL, }, }, // Connection pool settings // Adjust based on compute size });

    // For Drizzle with connection pool import { Pool } from '@neondatabase/serverless';

    const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 10, // Max connections in local pool idleTimeoutMillis: 30000, connectionTimeoutMillis: 10000, });

    // Compute size connection limits // 0.25 CU: 112 connections (105 available after reserved) // 0.5 CU: 225 connections // 1 CU: 450 connections // 2 CU: 901 connections // 4 CU: 1802 connections // 8 CU: 3604 connections

    anti_patterns:

    • pattern: "Opening new connection per request" why: "Exhausts connection limits quickly" fix: "Use connection pooling, reuse connections"

    • pattern: "High max pool size in serverless" why: "Many function instances = many pools = many connections" fix: "Keep local pool size low (5-10), rely on PgBouncer"

    references:

  • id: database-branching name: Database Branching for Development description: | Create instant copies of your database for development, testing, and preview environments.

    Branches share underlying storage (copy-on-write), making them instant and cost-effective.

    code_example: |

    Create branch via Neon CLI

    neon branches create --name feature/new-feature --parent main

    Create branch from specific point in time

    neon branches create --name debug/yesterday
    --parent main
    --timestamp "2024-01-15T10:00:00Z"

    List branches

    neon branches list

    Get connection string for branch

    neon connection-string feature/new-feature

    Delete branch when done

    neon branches delete feature/new-feature

    // In CI/CD (GitHub Actions) // .github/workflows/preview.yml name: Preview Environment on: pull_request: types: [opened, synchronize]

    jobs: create-branch: runs-on: ubuntu-latest steps: - uses: neondatabase/create-branch-action@v5 id: create-branch with: project_id: ${{ secrets.NEON_PROJECT_ID }} branch_name: preview/pr-${{ github.event.pull_request.number }} api_key: ${{ secrets.NEON_API_KEY }} username: ${{ secrets.NEON_ROLE_NAME }}

        - name: Run migrations
          env:
            DATABASE_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
          run: npx prisma migrate deploy
    
        - name: Deploy to Vercel
          env:
            DATABASE_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
          run: vercel deploy --prebuilt
    

    // Cleanup on PR close on: pull_request: types: [closed]

    jobs: delete-branch: runs-on: ubuntu-latest steps: - uses: neondatabase/delete-branch-action@v3 with: project_id: ${{ secrets.NEON_PROJECT_ID }} branch: preview/pr-${{ github.event.pull_request.number }} api_key: ${{ secrets.NEON_API_KEY }}

    anti_patterns:

    • pattern: "Sharing production database for development" why: "Risk of data corruption, no isolation" fix: "Create development branches from production"

    • pattern: "Not cleaning up old branches" why: "Accumulates storage and clutter" fix: "Auto-delete branches on PR close"

    references:

  • id: vercel-integration name: Vercel Preview Environment Integration description: | Automatically create database branches for Vercel preview deployments. Each PR gets its own isolated database.

    Two integration options:

    • Vercel-Managed: Billing in Vercel, auto-setup
    • Neon-Managed: Billing in Neon, more control

    code_example: |

    Vercel-Managed Integration

    1. Go to Vercel Dashboard > Storage > Create Database

    2. Select Neon Postgres

    3. Enable "Create a branch for each preview deployment"

    4. Environment variables automatically injected

    Neon-Managed Integration

    1. Install from Neon Dashboard > Integrations > Vercel

    2. Select Vercel project to connect

    3. Enable "Create a branch for each preview deployment"

    4. Optionally enable auto-delete on branch delete

    // vercel.json - Add migration to build { "buildCommand": "prisma migrate deploy && next build", "framework": "nextjs" }

    // Or in package.json { "scripts": { "vercel-build": "prisma generate && prisma migrate deploy && next build" } }

    // Environment variables injected by integration // DATABASE_URL - Pooled connection for preview branch // DATABASE_URL_UNPOOLED - Direct connection for migrations // PGHOST, PGUSER, PGDATABASE, PGPASSWORD - Individual vars

    // Prisma schema for Vercel integration datasource db { provider = "postgresql" url = env("DATABASE_URL") directUrl = env("DATABASE_URL_UNPOOLED") // Vercel variable }

    // For Drizzle in Next.js on Vercel import { neon } from '@neondatabase/serverless'; import { drizzle } from 'drizzle-orm/neon-http';

    // Use pooled URL for queries const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql);

    anti_patterns:

    • pattern: "Same database for all previews" why: "Previews interfere with each other" fix: "Enable branch-per-preview in integration"

    • pattern: "Not running migrations on preview" why: "Schema mismatch between code and database" fix: "Add migrate command to build step"

    references:

  • id: autoscaling-cold-starts name: Autoscaling and Cold Start Management description: | Neon autoscales compute resources and scales to zero.

    Cold start latency: 500ms - few seconds when waking from idle. Production recommendation: Disable scale-to-zero, set minimum compute.

    code_example: |

    Neon Console settings for production

    Project Settings > Compute > Default compute size

    - Set minimum to 0.5 CU or higher

    - Disable "Suspend compute after inactivity"

    // Handle cold starts in application // lib/db-with-retry.ts import { prisma } from './prisma';

    const MAX_RETRIES = 3; const RETRY_DELAY = 1000;

    export async function queryWithRetry<T>( query: () => Promise<T> ): Promise<T> { let lastError: Error | undefined;

    for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
      try {
        return await query();
      } catch (error) {
        lastError = error as Error;
    
        // Retry on connection errors (cold start)
        if (error.code === 'P1001' || error.code === 'P1002') {
          console.log(`Retry attempt ${attempt}/${MAX_RETRIES}`);
          await new Promise(r => setTimeout(r, RETRY_DELAY * attempt));
          continue;
        }
    
        throw error;
      }
    }
    
    throw lastError;
    

    }

    // Usage const users = await queryWithRetry(() => prisma.user.findMany() );

    // Reduce cold start latency with SSL direct negotiation

    PostgreSQL 17+ connection string

    postgres://user:pass@ep-xxx-pooler.aws.neon.tech/db?sslmode=require&sslnegotiation=direct

    // Keep-alive for long-running apps // lib/db-keepalive.ts import { prisma } from './prisma';

    // Ping database every 4 minutes to prevent suspend const KEEPALIVE_INTERVAL = 4 * 60 * 1000;

    if (process.env.NEON_KEEPALIVE === 'true') { setInterval(async () => { try { await prisma.$queryRaw

    SELECT 1
    ; } catch (error) { console.error('Keepalive failed:', error); } }, KEEPALIVE_INTERVAL); }

    // Compute sizing recommendations // Development: 0.25 CU, scale-to-zero enabled // Staging: 0.5 CU, scale-to-zero enabled // Production: 1+ CU, scale-to-zero DISABLED // High-traffic: 2-4 CU minimum, autoscaling enabled

    anti_patterns:

    • pattern: "Scale-to-zero in production" why: "Cold starts add 500ms+ latency to first request" fix: "Disable scale-to-zero for production branch"

    • pattern: "No retry logic for cold starts" why: "First connection after idle may timeout" fix: "Add retry with exponential backoff"

    references:

handoff_triggers:

  • condition: "needs authentication" target_skill: clerk-auth context: "User table with clerkId"

  • condition: "needs caching" target_skill: redis-specialist context: "Query caching, session storage"

  • condition: "needs search" target_skill: algolia-search context: "Full-text search beyond Postgres"

  • condition: "needs analytics" target_skill: data-engineer context: "Data warehouse, analytics queries"