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

By netlify · 2,107 installs

npx skills add netlify/context-and-tools --skill netlify-database

Source repository · Upstream listing

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) Own driver / ORM instead: 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 What Location Migrations netlify/database/migrations/ (SQL files or subdirs with migration.sql ) Query code Functions ( netlify/functions/ ), Edge Functions Drizzle schema db/schema.ts (convention) Drizzle client db/index.ts (convention) Connection string NETLIFY 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. 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 ](https://node postgres.com/apis/pool). BEGIN /queries/ COMMIT must run on the same connection: Own drivers: Drizzle ORM Install both packages from @beta — required. latest lacks the drizzle orm/netlify db adapter and will fail. drizzle.config.ts — you MUST set out to the Netlify migrations directory or Netlify won't apply generated migrations: 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: 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. 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: Or the Vite plugin: Common commands (while local DB is running): External tools (works while netlify dev runs): 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: 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 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 . Command Purpose Key flags init Set up DB in project y, yes status State: enabled, installed, connection string, applied/pending migrations b, branch , show credentials connect SQL REPL, or query one shot q, query , json migrations apply Apply pending to local DB to <name migrations new Scaffold a migration d, description , s, scheme sequential\ timestamp migrations pull Overwrite local files from a branch b, branch , force migrations reset Delete unapplied local migration files b, branch reset Drop 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 + path Purpose POST /sites/{site id}/database Create DB (returns existing conn string if present); region optional GET /sites/{site id}/database Get connection string POST /sites/{site id}/database/branch Create 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/snapshot Snapshot a branch (defaults production) GET /sites/{site id}/database/snapshots List snapshots DELETE /sites/{site id}/database/snapshot/{snapshot id} Delete a snapshot POST /sites/{site id}/database/snapshot/{snapshot id}/restore Restore 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): 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): 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. 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): 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