netlify/context-and-tools/netlify-database

Claude Code and agent skill by netlify/context-and-tools

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

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.

Install

npx vskill@latest install netlify/context-and-tools/netlify-database

SKILL.md

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)

ts
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:

ts
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.

ts
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:

ts
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:

ts
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.

bash
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:

ts title="drizzle.config.ts"
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./db/schema.ts",
out: "netlify/database/migrations", // NOT the default "drizzle"
});
ts title="db/schema.ts"
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(),
});
ts title="db/index.ts"
import { drizzle } from "drizzle-orm/netlify-db"; // native adapter, auto-configured
import * as schema from "./schema";
export const db = drizzle({ schema });
ts title="netlify/functions/api.ts"
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:

text
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.

sql title="netlify/database/migrations/20260425103000_create_comments.sql"
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:

bash
netlify dev # CLI starts + tears down the local DB

Or the Vite plugin:

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

Common commands (while local DB is running):

bash
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):

bash
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:

bash
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.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
34
Repository forks
7
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 12 Aug 2026, 2 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/netlify/context-and-tools/netlify-database/badge)](https://verified-skill.com/skills/netlify/context-and-tools/netlify-database)

Something wrong with this skill or its page? Tell us.