MCPInstall

netlify-database

Zero-config Postgres for Netlify apps via @netlify/database — querying data from Functions/Edge Functions, writing schema migrations, setting up Drizzle ORM, local dev with netlify dev, database branches for deploy previews, and migrating an existing Postgres project onto Netlify. Use when adding a database, building a contact form or CRUD API, writing SQL migrations, wiring up Drizzle, running netlify database commands, testing with a local Postgres, or switching from Neon/Supabase/RDS to Netlify Database.

Installs
2.9K
Last 24h
+20
Trending rank
#5872
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/netlify/context-and-tools --skill netlify-database
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads netlify-database automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Netlify Database

Zero-config managed Postgres. Install @netlify/database, write migrations under netlify/database/migrations/, deploy — Netlify provisions the DB and applies migrations automatically. Queryable from Functions, Edge Functions, Builds, and Agent Runners.

Modern client (reach for this)

import { getDatabase } from "@netlify/database";

const db = getDatabase();               // auto-selects connection for the runtime
const userId = 42;
const users = await db.sql`SELECT * FROM users WHERE id = ${userId}`;  // auto-parameterized

Own driver / ORM instead:

import { getConnectionString } from "@netlify/database";
const connectionString = getConnectionString();  // correct branch for this env

Legacy — do NOT use for new code: import { neon } from "@netlify/neon". Superseded by @netlify/database. Replace neon() calls with the Drizzle netlify-db adapter or a Postgres driver via getConnectionString(). The legacy env var NETLIFY_DATABASE_URL is replaced by NETLIFY_DB_URL.

Where things go

WhatLocation
Migrationsnetlify/database/migrations/ (SQL files or subdirs with migration.sql)
Query codeFunctions (netlify/functions/), Edge Functions
Drizzle schemadb/schema.ts (convention)
Drizzle clientdb/index.ts (convention)
Connection stringNETLIFY_DB_URL env var, or getConnectionString()

Querying

getDatabase(options?) returns a client with sql and pool. options.connectionString overrides the auto-provisioned one; options.debug enables logging.

const db = getDatabase();
const active = await db.sql`SELECT * FROM users WHERE active = ${true}`;
await db.sql`INSERT INTO users (name, email) VALUES (${"Ada"}, ${"ada@example.com"})`;
await db.sql`UPDATE users SET name = ${"Ada Lovelace"} WHERE id = ${1}`;
await db.sql`DELETE FROM users WHERE id = ${1}`;

// Type the rows
interface User { id: number; name: string; email: string; }
const typed = await db.sql<User>`SELECT * FROM users`;

// Stream
for await (const row of db.sql`SELECT * FROM users`.stream()) { /* ... */ }
for await (const chunk of db.sql`SELECT * FROM users`.chunked(100)) { /* ... */ }

SQLTemplate methods: execute() → Promise<T[]>, stream() → AsyncGenerator<T>, chunked(n) → AsyncGenerator<T[]>, toSQL() → raw SQL + params without executing.

sql helpers:

  • sql.identifier(value) — safe table/column name. String, string[], or { schema, table, column, as }.
  • sql.values(rows) — bulk-insert values list from a 2D array.
  • sql.default — the SQL DEFAULT keyword.
  • sql.raw(value) — injects unparameterized SQL; bypasses injection protection. Only for trusted constants (e.g. "DESC"), never user input.
  • sql.unsafe(query, params?, { rowMode }) — raw query string with $1 params; rowMode is "array" or "object".

Transactions — use pool

db.pool is a pg.Pool. BEGIN/queries/COMMIT must run on the same connection:

const client = await db.pool.connect();
try {
  await client.query("BEGIN");
  await client.query("INSERT INTO users (name, email) VALUES ($1, $2)", ["Ada", "ada@example.com"]);
  await client.query("INSERT INTO posts (author_id, title) VALUES ($1, $2)", [1, "First post"]);
  await client.query("COMMIT");
} catch (e) {
  await client.query("ROLLBACK");
  throw e;
} finally {
  client.release();
}

Own drivers:

import { getConnectionString } from "@netlify/database";
import pg from "pg";
const pool = new pg.Pool({ connectionString: getConnectionString() });

// or the `postgres` driver via env var
import postgres from "postgres";
const sql = postgres(process.env.NETLIFY_DB_URL);

Drizzle ORM

Install both packages from @beta — required. latest lacks the drizzle-orm/netlify-db adapter and will fail.

npm install @netlify/database drizzle-orm@beta
npm install -D drizzle-kit@beta

drizzle.config.ts — you MUST set out to the Netlify migrations directory or Netlify won't apply generated migrations:

import { defineConfig } from "drizzle-kit";
export default defineConfig({
  dialect: "postgresql",
  schema: "./db/schema.ts",
  out: "netlify/database/migrations",   // NOT the default "drizzle"
});
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
  id: serial().primaryKey(),
  name: text().notNull(),
  email: text().notNull().unique(),
  createdAt: timestamp().defaultNow(),
});
import { drizzle } from "drizzle-orm/netlify-db";  // native adapter, auto-configured
import * as schema from "./schema";
export const db = drizzle({ schema });
import { desc } from "drizzle-orm";
import type { Config, Context } from "@netlify/functions";
import { db } from "../../db";
import { users } from "../../db/schema";

export default async (req: Request, context: Context) => {
  if (req.method === "GET") {
    const allUsers = await db.select().from(users).orderBy(desc(users.createdAt));
    return Response.json(allUsers);
  }
  if (req.method === "POST") {
    const { name, email } = await req.json();
    const [user] = await db.insert(users).values({ name, email }).returning();
    return Response.json(user, { status: 201 });
  }
  return new Response("Method not allowed", { status: 405 });
};

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

Generate migrations after editing the schema: npx drizzle-kit generate.

Never run drizzle-kit push against a Netlify-hosted database, and never run drizzle-kit migrate against NETLIFY_DB_URL. Schema reaches hosted DBs only as committed migration files applied by the deploy. generate writes files; the deploy applies them.

Migrations

Files live in netlify/database/migrations/. Two formats:

netlify/database/migrations/20260301143000_create_users.sql          # single SQL file
netlify/database/migrations/20260318091500_add_posts/migration.sql   # subdir form

Naming: <number>_<slug> — number is digits (timestamp or 0001…) defining order; slug is lowercase letters/numbers/hyphens/underscores. Sorted lexicographically, applied in order. Use timestamp prefixes (netlify database migrations new handles this) to avoid out-of-order rejection.

CREATE TABLE comments (
  id SERIAL PRIMARY KEY,
  post_id INTEGER NOT NULL REFERENCES posts(id),
  author_id INTEGER NOT NULL REFERENCES users(id),
  body TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

When applied:

  • Production deploy: applied immediately before publish; a failure blocks publish. With auto-publish off, Netlify waits for manual publish before applying.
  • Deploy preview: applied on every deploy before it goes live; a failure fails the deploy.
  • Local: not automatic — run netlify database migrations apply yourself.

Migration footguns (all detected as drift / rejected):

  • Never edit an applied migration — checksum drift: migration "<name>" has been modified after being applied. Write a new corrective migration.
  • Never remove an applied migration — ... has been removed after being applied. Restore it.
  • Out-of-order: a prefix ≤ the highest applied version is rejected. Timestamps avoid this.
  • Prefer backwards-compatible migrations. Breaking changes (rename/drop column) → expand-and-contract across multiple deploys. New table / nullable column → single migration is fine.

Bring-your-own migration system: pick a directory other than netlify/database/migrations to avoid automatic detection, and you own applying to preview branches and production.

See references/migrations.md.

Local development

Local is one database that all code targets — branches are a deploy-time concept and don't exist locally. It's a real Postgres-compatible engine mirroring production, but single-process (not for load testing); auto-scale/sleep settings don't apply.

Start it — either path, state is interchangeable:

netlify dev                                    # CLI starts + tears down the local DB

Or the Vite plugin:

import { defineConfig } from "vite";
import netlify from "@netlify/vite-plugin";
export default defineConfig({ plugins: [netlify()] });

Common commands (while local DB is running):

netlify database migrations apply                        # apply pending locally
netlify database migrations new -d "add users table"     # scaffold new migration
netlify database migrations pull                          # overwrite local migrations from remote
netlify database status                                   # enabled? installed? applied/pending migrations
netlify database connect                                  # interactive SQL REPL
netlify database connect --query "SELECT * FROM users LIMIT 10"
netlify database reset                                    # drop all schemas/tables — LOCAL ONLY
netlify database migrations reset                         # delete unapplied local migration files

External tools (works while netlify dev runs):

psql "$(netlify database connect --json | jq -r .connection_string)"

See references/local-dev.md.

Setup

New project: describe your app to Agent Runners at https://app.netlify.com/start, or netlify create "<description>" locally.

Existing project:

netlify database init      # installs @netlify/database, picks Drizzle or raw SQL, scaffolds a migration
netlify database init --yes # non-interactive (CI / agents)
netlify dev

Manual: npm install @netlify/database, write a migration under netlify/database/migrations/, write a function, netlify dev, deploy.

If @netlify/database is NOT installed, Netlify will NOT auto-provision a database — you'd have to create one manually from the UI Data & Storage > Database menu. Install the package.

CLI reference (netlify database)

Prereqs: Node ≥ 20.12.2, Netlify CLI ≥ 26.0.0 (npm install -g netlify-cli). All commands support --json.

CommandPurposeKey flags
initSet up DB in project-y, --yes
statusState: enabled, installed, connection string, applied/pending migrations-b, --branch, --show-credentials
connectSQL REPL, or --query one-shot-q, --query, --json
migrations applyApply pending to local DB--to <name>
migrations newScaffold a migration-d, --description, -s, --scheme sequential|timestamp
migrations pullOverwrite local files from a branch-b, --branch, --force
migrations resetDelete unapplied local migration files-b, --branch
resetDrop all data/tables — local only—

See references/cli-commands.md.

REST API

Scoped to a site, rooted at https://api.netlify.com/api/v1, OAuth 2. Full reference: https://open-api.netlify.com.

Method + pathPurpose
POST /sites/{site_id}/databaseCreate DB (returns existing conn string if present); region optional
GET /sites/{site_id}/databaseGet connection string
POST /sites/{site_id}/database/branchCreate branch; body deploy_id (req), parent_branch_id (opt, defaults to production)
GET /sites/{site_id}/database/branch/{deploy_id}Get branch conn string (404 if none)
DELETE /sites/{site_id}/database/branch/{deploy_id}Delete a deploy's branch
POST /sites/{site_id}/database/snapshotSnapshot a branch (defaults production)
GET /sites/{site_id}/database/snapshotsList snapshots
DELETE /sites/{site_id}/database/snapshot/{snapshot_id}Delete a snapshot
POST /sites/{site_id}/database/snapshot/{snapshot_id}/restoreRestore snapshot to a branch (defaults production)

Branch delete and snapshot restore are destructive and require explicit user confirmation first. Snapshot restore is not a routine production-rollback lever.

Testing

Bare Postgres for unit/integration tests (no functions):

import { NetlifyDB } from "@netlify/database-dev";  // npm i -D @netlify/database-dev
import { Client } from "pg";
import { afterAll, beforeAll, expect, test } from "vitest";

let db: NetlifyDB, connectionString: string;
beforeAll(async () => {
  db = new NetlifyDB();
  connectionString = await db.start();
  await db.applyMigrations("./netlify/database/migrations");
});
afterAll(async () => { await db.stop(); });

test("inserts and reads a user", async () => {
  const client = new Client({ connectionString });
  await client.connect();
  await client.query("INSERT INTO users (name) VALUES ($1)", ["Ada"]);
  const { rows } = await client.query("SELECT name FROM users");
  expect(rows).toEqual([{ name: "Ada" }]);
  await client.end();
});

NetlifyDB(options?): directory (persist to disk; omit = in-memory), port (default random), logger.

Full Netlify environment (functions/edge functions read NETLIFY_DB_URL as in production):

import { NetlifyDev } from "@netlify/dev";  // npm i -D @netlify/dev
const netlifyDev = new NetlifyDev({ projectRoot: "./fixtures/my-project" });
await netlifyDev.start();  // sets NETLIFY_DB_URL in the runtime
// ...tests...
await netlifyDev.stop();

Database branches (deploy-time)

Production deploys are the only deploys that touch the production database. Each deploy preview gets its own branch, seeded with a copy of production data at preview-creation time; schema/data changes there never affect production. Wired up automatically, no code changes.

Preview branches can contain production data, including PII — and preview deploy links are public. Warn the user before sharing a preview link.

Runtime gotchas

  • Environment not configured (getDatabase() can't resolve a connection string): running outside Netlify, on Functions in Lambda compatibility mode, or an outdated CLI. Fix: pass connectionString explicitly.
    const db = getDatabase({ connectionString: "postgres://..." });
    
    Lambda compatibility mode is the one primitive where you must pass connectionString yourself.
  • database feature not available for this account — requires a Credit-based plan.
  • compute customization requires a Pro or higher plan — auto-scale / sleep settings need Pro+; Free/Personal use defaults.
  • branch limit reached: maximum <N> branches... — each active deploy preview consumes a branch; delete unneeded branches or upgrade.
  • database not found — no DB provisioned; run netlify database init.
  • cannot reset the production branch — reset is non-production only.

Constraints

  • Plan: Netlify Database is available on Credit-based plans only; active DBs consume credits for compute and bandwidth. Storage is free until July 1, 2026.
  • Permissions: only a Team Owner can delete a database; only Team Owners and Developers can view connection strings (Access Denied = insufficient role).
  • Secrets: connection strings contain username + password. Never commit them; store in a secret manager / env var provider.

Switch an existing Postgres project to Netlify Database

Three phases: provision (baseline schema on a branch), rehearse (swap code, copy data into a preview branch, validate), cut over (import data into production, merge). Works from any Postgres source (Neon, Supabase, RDS, self-managed, legacy @netlify/neon). Uses pg_dump/pg_restore (versions matching the source). There is a brief data-loss window — writes to the source between final export and production deploy don't cross over.

Phase 2/3 code swap (Drizzle):

import { drizzle } from "drizzle-orm/netlify-db";
import * as schema from "./schema";
export const db = drizzle({ schema });

Full step-by-step (dump flags, rollback, cleanup): references/migration-from-extension.md and references/legacy-extension.md.

<!-- Gaps: plan-tier naming (Credit-based vs Free/Personal/Pro) not reconciled in source; exact plan limits, permission tables, and snapshot UI flows live on pages outside this grouping. --> <!-- system: agent-context/database/system.md — human-owned, merged by ctx-gen; edit system.md, not this section -->

Netlify house rules (database)

These are org conventions, not docs facts — merged into the rendered skill by ctx-gen and never generated. Owned by the skills maintainer.

  1. Production data changes are expressed as DML migrations — agents never edit rows directly (UI row editing exists for humans; it is not an agent surface).
  2. Preview branches can contain production data, including PII — and preview deploy links are public. Warn before sharing.
  3. Use only documented surfaces: no raw psql against internal endpoints, no netlify api scraping, no reading tokens from local CLI config files.
  4. Deep guides live in this skill: references/operational-footguns.md, references/migrations.md, references/local-dev.md, references/cli-commands.md, references/migration-from-extension.md, references/legacy-extension.md.
  5. Schema changes reach hosted databases only as committed migration files applied by the deploy. Never run drizzle-kit push in any form against a Netlify-hosted database, never run drizzle-kit migrate against NETLIFY_DB_URL, and never apply DDL via netlify database connect or any direct connection.
  6. When a netlify command or a deploy fails, surface the exact error, the deploy log URL, and the affected site/branch to the user and stop — do not invent recovery commands or escalate to lower-level tools.
  7. First-deploy 401 Access Denied on createSiteDatabase: if it happened on a --prod-first deploy, retry preview-first (netlify deploy, no --prod); if a preview also fails, report and stop. Never curl api.netlify.com, run netlify api createSiteDatabase, or pull tokens from local CLI config to work around it.
  8. A request to change existing data is ambiguous between production and the preview branch — if the prompt didn't say, ask. When acting on someone's behalf, default to not touching production.
  9. Destructive database operations — REST branch delete, snapshot restore, any reset — require explicit user confirmation first. The body must not present snapshot restore as a routine production-rollback lever.
  10. Pin: drizzle-orm and drizzle-kit must be installed from @beta — latest lacks the drizzle-orm/netlify-db adapter and will fail. The body may not soften this to a recommendation.

FAQ

What does the netlify-database skill do?

Zero-config Postgres for Netlify apps via @netlify/database — querying data from Functions/Edge Functions, writing schema migrations, setting up Drizzle ORM, local dev with netlify dev, database branches for deploy previews, and migrating an existing Postgres project onto Netlify. Use when adding a database, building a contact form or CRUD API, writing SQL migrations, wiring up Drizzle, running netlify database commands, testing with a local Postgres, or switching from Neon/Supabase/RDS to Netlify Database.

How do I install netlify-database?

Run npx skills add https://github.com/netlify/context-and-tools --skill netlify-database in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/netlify-database/ (personal) or .claude/skills/netlify-database/ (project).

Which AI agents can use netlify-database?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is netlify-database safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains netlify-database?

It is published by netlify in netlify/context-and-tools. It has 2,936 recorded installs.

More from netlify/context-and-tools

View all →

netlify-deploy

netlify/context-and-tools

Create, configure, and manage Netlify deploys from code — reach for this when setting up Git continuous deployment, running netlify deploy or netlify deploy --prod from the CLI, writing netlify.toml deploy contexts, adding a Deploy to Netlify button, wiring build hooks, configuring Deploy Previews or branch deploys, locking or skipping deploys, fixing a failed or secrets-scanning deploy, or when someone asks to "deploy my site", "set up preview deploys", "add per-branch build config", "keep staging out of Google", or "add a deploy button to my README".

DevOps & Cloud3.4K installs

netlify-config

netlify/context-and-tools

Configure Netlify builds and routing via netlify.toml, redirects, and headers. Use when setting a build command or publish directory, adding redirects or rewrites or proxies, adding an SPA fallback rewrite, setting custom response headers or basic auth, managing environment variables and secrets, scoping vars per deploy context, marking a var as secret, disabling secret scanning, configuring functions bundling, ignoring builds, or wiring up a monorepo or JavaScript SPA on Netlify.

DevOps & Cloud3.3K installs

netlify-functions

netlify/context-and-tools

Write, configure, and deploy Netlify serverless functions in TypeScript, JavaScript, or Go. Use this when adding an API endpoint or backend route, adding a contact form handler, wiring auth or Identity signup/login hooks, identifying which Netlify user is calling a private or team-login-protected app, building streaming or AI-proxy responses, scheduling cron jobs, running long background jobs (batch processing/scraping), reacting to deploy or form events, setting up rate limiting or region/memory config, or reading environment variables and secrets inside a function. Covers file locations, the Request/Context/Response handler shape, path routing, config options, and local testing with netlify dev.

DevOps & Cloud3.3K installs

netlify-frameworks

netlify/context-and-tools

Deploy and configure web frameworks on Netlify — build settings and SSR/edge adapters plus local platform emulation and env vars. Use when setting up or fixing a framework deploy (Next.js / Astro / Nuxt / SvelteKit / Remix / React Router / TanStack Start / SolidStart / Gatsby / Angular / Vite / Express / Hydrogen / Hugo / Eleventy / Vue / React), adding SSR or edge functions or middleware wired to Netlify context, fixing SPA redirect and catch-all rules, setting a build command or publish directory, or debugging "why isn't my env var updating" and framework build failures.

DevOps & Cloud3.2K installs

netlify-edge-functions

netlify/context-and-tools

Write, route, cache, and debug Netlify Edge Functions, the Deno-based TypeScript or JavaScript handlers in netlify/edge-functions that run at the network edge in front of static files and serverless functions. Activate this skill whenever someone wants to add auth middleware or a login gate, redirect or rewrite visitors by country or cookie, localize or personalize a page with geolocation, run A/B testing, transform or inject HTML into a response, stream server-side rendering at the edge, or cache an edge response. Also use it when an edge function silently does nothing, runs in the wrong order, ignores its Cache-Control headers, or cannot read an environment variable.

DevOps & Cloud3.1K installs

netlify-forms

netlify/context-and-tools

Serverless form handling on Netlify-hosted sites — detects HTML forms at deploy time, stores submissions, filters spam, and sends notifications. Use when adding a contact form, lead-capture form, file-upload form, or newsletter signup to a Netlify site; wiring AJAX form submission; setting up a custom thank-you page; adding a honeypot or reCAPTCHA to a form; getting forms working in Next.js, Nuxt, SvelteKit, Astro, or Gatsby; reading form submissions via the Netlify API; setting up submission notifications or ChatGPT automations on form submission events; or debugging missing submissions and forms that silently fail to register.

DevOps & Cloud3.1K installs

Popular data & databases skills

View category →

Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.

OfficialData & Databases462.2K installs

prisma-client-api

prisma/skills

Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".

OfficialData & Databases393.5K installs

Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.

OfficialData & Databases392.3K installs

prisma-cli

prisma/skills

Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.

OfficialData & Databases390.8K installs

prisma-postgres

prisma/skills

Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.

OfficialData & Databases384.4K installs

Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.

OfficialData & Databases378.9K installs