# How to Integrate Bolt.new with PostgreSQL

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

The standard pg (node-postgres) driver uses TCP and fails in Bolt's WebContainer during development. PostgreSQL in Bolt.new works through three HTTP-based alternatives: Bolt Database (built-in, auto-provisioned Supabase PostgreSQL), Supabase (@supabase/supabase-js via PostgREST), or Neon (@neondatabase/serverless via HTTP/WebSocket). All three work in the preview. After deployment to Netlify, standard pg driver connects normally — TCP works in a real Node.js runtime.

## PostgreSQL in Bolt.new: Three HTTP Paths to the Same Database

PostgreSQL is the default database choice for most web apps — it is what Supabase, Neon, Railway, and Bolt's own database are built on. The challenge in Bolt.new is architectural: the standard node-postgres driver (pg) communicates with PostgreSQL servers via raw TCP connections on port 5432. Bolt's WebContainer virtualizes Node.js inside the browser and cannot make raw TCP connections. When you install pg and call pool.query('SELECT...'), the TCP connection attempt fails silently or throws a connection refused error during development.

The good news: PostgreSQL has the most HTTP-compatible ecosystem of any database in the Bolt world. Three services provide HTTP-over-PostgreSQL access that works in the WebContainer. Bolt Database, introduced in September 2025, is built-in — if you ask Bolt to add a database to your project, it provisions one automatically using Supabase infrastructure. No signup, no configuration, it just works. Supabase exposes its PostgreSQL instances through PostgREST, an HTTP REST API layer, accessible via @supabase/supabase-js. Neon provides a serverless PostgreSQL driver (@neondatabase/serverless) that switches between HTTP and WebSocket modes — the HTTP mode works in WebContainers.

For apps that specifically need raw SQL through the pg driver — complex JOINs, stored procedures, custom functions — deploy to Netlify and use pg with a Neon, Supabase, or Railway connection string. After deployment, the TCP connection works normally. The WebContainer limitation applies only during local Bolt development, not production.

## Before you start

- A Bolt.new project — Vite + React works for Supabase/Neon, Next.js needed for API routes with raw SQL
- For Bolt Database: no prerequisites — Bolt provisions it automatically when you ask
- For Supabase: a Supabase account at supabase.com (free tier includes 2 projects and 500MB storage)
- For Neon: a Neon account at neon.tech (free tier includes 3 projects and 0.5GB storage)
- Understanding that the pg driver works only after deploying to Netlify — use HTTP-based clients during Bolt development

## Step-by-step guide

### 1. Choose Your PostgreSQL Path and Understand the TCP Limitation

Before writing any code, understand the WebContainer constraint and pick the right approach for your use case. Bolt's WebContainer runs Node.js inside a browser tab via WebAssembly. The browser security model prevents raw TCP socket connections, which means the standard pg (node-postgres) driver cannot connect to PostgreSQL servers during development. When you run pool.query() in the Bolt preview, the call will hang or throw a connection error — not because your connection string is wrong, but because TCP is blocked at the WebAssembly layer.

Here is the decision tree for choosing your PostgreSQL approach:

**Option 1: Bolt Database (auto-provisioned)** — Best for new projects where you want zero setup. Bolt provisions a Supabase PostgreSQL database automatically. Use @supabase/supabase-js for queries. Works in preview. Limitation: Supabase JS client works only with Vite projects per current Bolt documentation, not Next.js within Bolt's WebContainer.

**Option 2: Supabase + @supabase/supabase-js** — Best for apps needing auth, real-time, and storage alongside the database. Use with Vite. PostgREST exposes your tables as REST endpoints over HTTP. Works in preview.

**Option 3: Neon + @neondatabase/serverless** — Best for raw SQL with Vite or Next.js. Neon's serverless driver has HTTP mode that works in WebContainer. Supports tagged template literals for safe SQL. Works in preview.

**Option 4: pg driver after deployment** — Best when you need connection pooling, transactions, or stored procedures that require the full pg driver. Write the code in Bolt, test by deploying to Netlify. This is the path for production apps with complex query requirements.

All four paths lead to the same PostgreSQL — the difference is the client library and when you can test it. For most Bolt apps, start with Option 2 or 3.

```
// Summary of PostgreSQL options in Bolt.new:

// OPTION 1: Supabase (works in Bolt preview - Vite projects only)
// Uses PostgREST over HTTP
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_ANON_KEY
);
// Usage: await supabase.from('products').select('*')

// OPTION 2: Neon serverless (works in Bolt preview)
// Uses HTTP transport
import { neon } from '@neondatabase/serverless';
const sql = neon(import.meta.env.VITE_DATABASE_URL);
// Usage: await sql`SELECT * FROM products WHERE id = ${id}`

// OPTION 3: Standard pg driver (FAILS in Bolt preview - TCP blocked)
// Use ONLY in deployed Next.js API routes, NOT in browser/Vite
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// Usage: await pool.query('SELECT * FROM products WHERE id = $1', [id])
// Error in Bolt preview: ECONNREFUSED / timeout (TCP blocked by WebContainer)
```

**Expected result:** You understand which PostgreSQL connection method to use for your project type and have chosen between Bolt Database, Supabase, Neon serverless, or standard pg for post-deployment.

### 2. Option A: Set Up Neon Serverless PostgreSQL

Neon is a serverless PostgreSQL provider that offers the best balance of flexibility and WebContainer compatibility for Bolt apps. Create a free account at neon.tech and provision a new database. On the Neon dashboard, find your connection string under 'Connection Details' — it looks like postgresql://user:password@ep-something.us-east-2.aws.neon.tech/neondb.

Install @neondatabase/serverless. The package exports a neon function that creates a tagged template literal query executor using HTTP mode — no TCP, fully WebContainer compatible. Import and initialize neon() with your connection string from an environment variable.

Neon's tagged template literal syntax automatically parameterizes query values, preventing SQL injection. Write queries like: const users = await sql`SELECT * FROM users WHERE email = ${email}`. The ${email} is a parameterized value, not string interpolation — safe and correct. For multiple results, the response is an array of row objects with typed properties matching your column names.

For Next.js projects using Neon with API routes: use the neon function in your Route Handler files (app/api/*/route.ts). For Vite projects: you can use neon directly in client components only if you include the DATABASE_URL with the VITE_ prefix — but this exposes your database credentials client-side. Prefer putting Neon queries in a server context or API layer whenever possible.

Neon also supports Prisma through the @prisma/adapter-neon package, which uses HTTP/WebSocket instead of TCP — making Prisma + Neon work in the WebContainer for a fully type-safe ORM experience.

```
// lib/db.ts — Neon serverless client
import { neon } from '@neondatabase/serverless';

const connectionString = process.env.DATABASE_URL;

if (!connectionString) {
  throw new Error('DATABASE_URL environment variable is required');
}

export const sql = neon(connectionString);

// Type helper for rows
export type QueryResult<T> = T[];

// app/api/users/route.ts — Using neon sql tag
import { NextResponse } from 'next/server';
import { sql } from '@/lib/db';

interface User {
  id: number;
  name: string;
  email: string;
  created_at: string;
}

export async function GET() {
  try {
    const users = await sql<User[]>`
      SELECT id, name, email, created_at
      FROM users
      ORDER BY created_at DESC
      LIMIT 50
    `;
    return NextResponse.json(users);
  } catch (error: unknown) {
    const e = error as { message: string };
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}

export async function POST(request: Request) {
  try {
    const { name, email } = await request.json();
    if (!name || !email) {
      return NextResponse.json({ error: 'name and email required' }, { status: 400 });
    }
    const [user] = await sql<User[]>`
      INSERT INTO users (name, email)
      VALUES (${name}, ${email})
      RETURNING *
    `;
    return NextResponse.json(user, { status: 201 });
  } catch (error: unknown) {
    const e = error as { message: string; code?: string };
    if (e.code === '23505') {
      return NextResponse.json({ error: 'Email already exists' }, { status: 409 });
    }
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** The Neon @neondatabase/serverless client connects over HTTP in the Bolt preview. API routes execute SQL queries and return results. The users table is created via the migration SQL.

### 3. Option B: Set Up Supabase PostgreSQL with Real-Time

Supabase is the recommended PostgreSQL option for Vite-based Bolt projects. Supabase provides PostgreSQL via PostgREST (a REST API layer) and real-time subscriptions via WebSocket — both protocols work in the WebContainer. Create a Supabase project at supabase.com, find your project URL and anon key in Settings → API.

Install @supabase/supabase-js. Initialize the client with your Supabase URL and anon key (the anon key is safe to use client-side — it's a public key protected by Row Level Security). The client's .from() method chains query operators: .select(), .eq(), .gt(), .order(), .limit() for filtering; .insert(), .update(), .delete() for mutations; .subscribe() for real-time.

Create your database tables either through Supabase's Table Editor GUI (supabase.com → your project → Table Editor) or by prompting Bolt to generate and run SQL migrations. Enable RLS (Row Level Security) on tables that contain user-specific data — without RLS, anyone with your anon key can read all rows. Bolt generates RLS policies automatically when you describe your access rules.

Supabase real-time works through WebSocket channels. Subscribe to table changes: supabase.channel('orders').on('postgres_changes', {event: 'INSERT', schema: 'public', table: 'orders'}, callback).subscribe(). The callback receives a payload with the new row data. Unsubscribe in React's useEffect cleanup to prevent memory leaks.

Important Bolt-specific limitation: @supabase/supabase-js in the WebContainer works with Vite projects. For Next.js API routes that need direct PostgreSQL access with complex queries, use @neondatabase/serverless instead of Supabase's client library.

```
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error('Missing VITE_SUPABASE_URL or VITE_SUPABASE_ANON_KEY');
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

// Migration SQL (run in Supabase SQL editor):
/*
CREATE TABLE todos (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  title TEXT NOT NULL,
  completed BOOLEAN NOT NULL DEFAULT false,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

ALTER TABLE todos ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users see own todos" ON todos
  FOR ALL USING (auth.uid() = user_id);
*/

// Real-time subscription pattern:
// const channel = supabase
//   .channel('todos-changes')
//   .on('postgres_changes', {
//     event: '*',
//     schema: 'public',
//     table: 'todos',
//     filter: `user_id=eq.${userId}`
//   }, (payload) => {
//     if (payload.eventType === 'INSERT') setTodos(prev => [payload.new as Todo, ...prev]);
//     if (payload.eventType === 'UPDATE') setTodos(prev => prev.map(t => t.id === payload.new.id ? payload.new as Todo : t));
//     if (payload.eventType === 'DELETE') setTodos(prev => prev.filter(t => t.id !== payload.old.id));
//   })
//   .subscribe();
//
// return () => supabase.removeChannel(channel); // Cleanup
```

**Expected result:** Supabase client connects over HTTP in the Bolt Vite preview. The todos table is created with RLS. The useTodos hook fetches and subscribes to real-time changes.

### 4. Post-Deployment: Using the Standard pg Driver

After deploying to Netlify or Bolt Cloud, the TCP restriction is lifted — your code runs in a real Node.js environment where pg connects normally to PostgreSQL. This opens up advanced database patterns not possible in the WebContainer: connection pooling, transactions, prepared statements, stored procedures, COPY commands for bulk operations, and listen/notify for pub/sub.

For serverless deployments on Netlify (and Vercel), use a connection pooler in front of your PostgreSQL database. Serverless functions create a new Node.js process for each request — without pooling, each function invocation would open a new PostgreSQL connection, quickly exhausting the database's connection limit. Neon's pooler endpoint (add -pooler to hostname) or PgBouncer handles this automatically. Supabase also exposes a pooler connection string in its dashboard under Settings → Database → Connection pooling.

The pg Pool pattern for Next.js API routes: create a connection pool in a module-level variable. Node.js caches module imports, so the Pool object persists across API route invocations in the same Lambda container — connections are reused rather than created fresh each time. Export the pool from a lib/db.ts file and import it in your API routes.

For Prisma users: Prisma works excellently with PostgreSQL after deployment. Use @prisma/adapter-neon for Neon (works in both WebContainer HTTP mode and deployed TCP mode), or configure the standard prisma/client with DATABASE_URL pointing to your deployed database. Run prisma db push or prisma migrate deploy in your Netlify build command to apply schema changes on deployment.

```
// lib/pg-pool.ts
// NOTE: This module uses TCP connections and only works AFTER DEPLOYMENT
// to Netlify, Vercel, or similar Node.js environments.
// In Bolt's WebContainer preview, queries will fail with ECONNREFUSED or timeout.
import { Pool } from 'pg';

const connectionString = process.env.DATABASE_URL;

if (!connectionString) {
  throw new Error('DATABASE_URL environment variable is required');
}

export const pool = new Pool({
  connectionString,
  max: 5, // Max connections in pool (keep low for serverless)
  idleTimeoutMillis: 30000, // Release idle connections after 30s
  connectionTimeoutMillis: 5000, // Fail fast if connection takes >5s
  ssl: { rejectUnauthorized: false }, // Required for Neon/Supabase/RDS
});

// Graceful shutdown
process.on('SIGTERM', () => pool.end());

// Helper for parameterized queries
export async function query<T>(
  text: string,
  params?: (string | number | boolean | null | undefined)[]
): Promise<T[]> {
  const { rows } = await pool.query(text, params);
  return rows as T[];
}

// Transaction helper
export async function withTransaction<T>(
  callback: (client: { query: (text: string, params?: unknown[]) => Promise<{ rows: T[] }> }) => Promise<T>
): Promise<T> {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const result = await callback(client);
    await client.query('COMMIT');
    return result;
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}
```

**Expected result:** lib/pg-pool.ts is set up with connection pooling configured for serverless deployment. The module is clearly commented as deployment-only. After deploying to Netlify with DATABASE_URL set, PostgreSQL queries execute successfully.

## Best practices

- Use @neondatabase/serverless for Bolt projects that need raw SQL — it works in both WebContainer (HTTP mode) and deployed environments, and has the most straightforward migration path to standard pg driver
- Always enable RLS on Supabase tables — without RLS, your VITE_SUPABASE_ANON_KEY grants public read access to all table rows to anyone who inspects your client-side bundle
- Use connection pooling (Neon pooler URL or PgBouncer) for serverless deployments — serverless functions create new processes per request and will exhaust PostgreSQL's connection limit without a pooler
- Store PostgreSQL connection strings in environment variables without NEXT_PUBLIC_ or VITE_ prefix — connection strings contain credentials that must never be exposed in client-side code
- Include ssl: { rejectUnauthorized: false } in pg Pool configuration for cloud PostgreSQL providers — without SSL configuration, connections to Neon, Supabase, and most managed PostgreSQL services will fail
- For Supabase real-time subscriptions, always clean up channels in useEffect return functions — memory leaks from unsubscribed channels are a common Bolt app issue
- Test with Neon's free tier for development and production — Neon's scale-to-zero model means you pay nothing for inactive databases, making it cost-effective for low-traffic Bolt apps

## Use cases

### Full-Stack App with Bolt's Built-In Database

The fastest path to a working PostgreSQL database in Bolt.new: ask Bolt to add a database and it automatically provisions a Supabase PostgreSQL instance, generates schema, creates RLS policies, and configures the client. No account signup, no configuration. Start building your app in minutes with a production-ready PostgreSQL database.

Prompt example:

```
Add a database to this app using Bolt's built-in database. Create a products table with columns: id (uuid primary key), name (text not null), description (text), price (numeric not null), category (text), stock_quantity (integer default 0), created_at (timestamp default now()). Add RLS so only authenticated users can read products and only admins can write. Generate TypeScript types for the products table. Create a getProducts() function and a createProduct(product) function using the Supabase client.
```

### Neon Serverless PostgreSQL with Raw SQL

Use Neon's serverless PostgreSQL for apps that need raw SQL queries — JOINs, aggregations, complex WHERE clauses — with an HTTP-compatible driver that works in Bolt's WebContainer. Neon's @neondatabase/serverless client uses HTTP mode for single queries and WebSocket mode for transactions.

Prompt example:

```
Set up Neon PostgreSQL with @neondatabase/serverless. Install the package. Create lib/db.ts that exports a neon function initialized with DATABASE_URL from env. Create an API route at app/api/products/route.ts that uses the neon tagged template literal to run: GET - SELECT id, name, price, category FROM products WHERE ($1::text IS NULL OR category = $1) ORDER BY created_at DESC LIMIT 50 with the category query param as $1. POST - INSERT INTO products (name, description, price, category) VALUES ($1, $2, $3, $4) RETURNING * with values from request body. Return results as JSON. Use TypeScript types for Product.
```

### Supabase PostgreSQL with Real-Time Subscriptions

Use Supabase for PostgreSQL with real-time features — subscribe to table changes and update your UI automatically when other users insert or update rows. Supabase's @supabase/supabase-js uses WebSocket for real-time, which works in Bolt's WebContainer. This is the best choice for collaborative apps, dashboards, and live data feeds.

Prompt example:

```
Connect to Supabase PostgreSQL with real-time updates. Create a lib/supabase.ts that initializes the Supabase client with VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY. Create an OrdersDashboard component that: fetches all pending orders on mount with supabase.from('orders').select('*').eq('status', 'pending'), subscribes to INSERT events on the orders table to add new orders to state in real time, subscribes to UPDATE events to update order status when changed, unsubscribes on component unmount. Show orders in a table with columns: order ID, customer name, amount, status, created time. New orders appear at the top without page refresh.
```

## Troubleshooting

### ECONNREFUSED or connection timeout when running PostgreSQL queries in the Bolt preview

Cause: The pg driver (or any TCP-based PostgreSQL client) is attempting a TCP connection to PostgreSQL's port 5432. Bolt's WebContainer blocks all TCP socket connections.

Solution: Switch to an HTTP-based alternative for development: @neondatabase/serverless (HTTP mode) or @supabase/supabase-js (PostgREST). If you specifically need the pg driver, deploy to Netlify and test there. The same code works after deployment.

```
// Replace pg driver (TCP, fails in Bolt):
// import { Pool } from 'pg';
// const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// With Neon serverless (HTTP, works in Bolt):
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
// await sql`SELECT * FROM users` — works in Bolt preview
```

### Supabase client returns 'Failed to fetch' or CORS error in Bolt

Cause: The Supabase URL is incorrect, the VITE_SUPABASE_ANON_KEY is wrong, or the Supabase project is paused (Supabase pauses free projects after 1 week of inactivity).

Solution: Verify VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY match your project in supabase.com → Settings → API. If the project is paused, click 'Restore project' in the Supabase dashboard. Paused projects reject all API calls until restored.

```
// Verify Supabase client initialization:
console.log('Supabase URL:', import.meta.env.VITE_SUPABASE_URL?.substring(0, 30));
// Should show: https://xxxxxxxxxxxx.supabase.co
// If undefined, the VITE_SUPABASE_URL env var is missing from .env
```

### Neon database returns 'endpoint is disabled' or connection string format error

Cause: Neon free tier endpoints sleep after 5 minutes of inactivity and wake on the next request (cold start takes 1-2 seconds). A connection string format error means a copy-paste issue with the connection string.

Solution: Neon cold starts are normal — the first query after inactivity takes 1-2 seconds longer. Handle this with a loading state in your UI. For the connection string, use the pooler endpoint from the Neon dashboard (labeled 'Pooled connection') to minimize cold start frequency.

### Supabase RLS blocks all queries and returns empty arrays

Cause: Row Level Security is enabled on the table but no policy allows the current user (or anonymous user with the anon key) to read rows.

Solution: Add an appropriate RLS policy for your use case. For development, temporarily disable RLS with ALTER TABLE tablename DISABLE ROW LEVEL SECURITY; (never in production). For production, add a policy: CREATE POLICY 'allow_all' ON tablename FOR SELECT USING (true); for public data, or auth.uid() = user_id for user-specific rows.

```
-- Quick RLS debug: add a permissive select policy for testing
CREATE POLICY "allow_read" ON your_table
  FOR SELECT USING (true);
-- Replace with appropriate policy for production:
-- FOR SELECT USING (auth.uid() = user_id);
```

## Frequently asked questions

### Why does pg fail in Bolt's WebContainer but work after deploying to Netlify?

Bolt's WebContainer runs Node.js inside a browser tab using WebAssembly. Browsers cannot make raw TCP socket connections — a security restriction that also prevents WebContainers from doing so. PostgreSQL's pg driver communicates via TCP to port 5432. After deploying to Netlify, your code runs in real Node.js on AWS Lambda with full networking capabilities, so TCP connections work normally. This is not a bug in pg or in your code.

### Which PostgreSQL provider works best with Bolt.new?

Neon and Supabase are the top choices. Neon is best for Next.js projects and raw SQL needs — @neondatabase/serverless HTTP mode works in WebContainers and supports parameterized queries. Supabase is best for Vite projects with real-time requirements — @supabase/supabase-js uses PostgREST and WebSocket, both WebContainer-compatible, plus includes auth, storage, and edge functions. Bolt Database (auto-provisioned in Bolt V2+) is the easiest path for new projects — no setup required.

### Can I use Prisma with PostgreSQL in Bolt.new?

Yes, with Neon's HTTP adapter. Use @prisma/adapter-neon which communicates via HTTP and WebSocket instead of TCP — this makes Prisma work in Bolt's WebContainer. Configure your Prisma client with the neonAdapter and VITE_DATABASE_URL. Standard Prisma with pg adapter uses TCP and only works after deployment. Drizzle ORM also works with Neon serverless and is a popular alternative.

### Does Supabase work with Next.js in Bolt.new?

Per current Bolt documentation, @supabase/supabase-js connects successfully in Vite projects but has reported issues in Next.js projects within the Bolt WebContainer. For Next.js projects in Bolt, use @neondatabase/serverless instead of Supabase's JS client for database queries. You can still use Supabase for authentication in Next.js apps using @supabase/auth-helpers-nextjs — the database query limitation applies to the supabase.from() table operations.

### How do I run database migrations in Bolt.new?

For Supabase: go to your Supabase project dashboard → SQL Editor, paste and run your CREATE TABLE statements. For Neon: use the Neon SQL editor in the dashboard, or run psql locally with the Neon connection string. In Bolt itself, you can prompt Bolt to generate migration SQL files and then paste them into your database provider's SQL editor. Bolt does not automatically run SQL migrations — you apply them manually in the database provider's console.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/postgresql
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/postgresql
