Databases

Prerequisites

The marker

import { db } from "@org/fluffy-chainsaw-db";

const pool = await db.connect("users");
// build time: "database `users` must exist"
// runtime:    resolves the descriptor and returns a ready pg.Pool (throws if not wired)

Returns a Connection:

interface Connection {
  auth: "password" | "proxy" | "gcp-iam";
  host: string;
  port: number;
  user: string;
  database: string;
  sslmode: string;            // libpq: "disable" | "require" | "verify-ca" | "verify-full"
  password?: string;          // ONLY for auth: "password"
  url?: string;               // postgres:// DSN, ONLY for auth: "password"
  connector?: {               // present for proxy/IAM; else null
    kind: string;             // e.g. "cloudsql"
    instanceConnectionName?: string; // "proj:region:instance"
    iamAuthn?: boolean;
  } | null;
}

Handle both auth shapes — branch on conn.auth:

auth password / url connector How to connect
"password" set null use them directly
"proxy" / "gcp-iam" absent by design set a proxy/connector mints a short-lived IAM token; never expect a DSN

Connecting

import { db } from "@org/fluffy-chainsaw-db";

const pool = await db.connect("users"); // a ready pg.Pool — auth handled inside

Works unchanged locally (fluffy-chainsaw local, password descriptor) and deployed (Cloud SQL IAM connector — no password anywhere). Pass pool tuning as a second arg: db.connect("users", { max: 10 }). pool.end() also releases the IAM connector.

Raw descriptor (opting out)

If you need to construct your own pg client or use a different driver/ORM, import db (or db as core) and sslOf from @org/fluffy-chainsaw. The raw core db.connect(name) returns the Connection descriptor.

Branch on conn.auth to handle both auth shapes:

auth password / url connector How to connect
"password" set null use them directly
"proxy" / "gcp-iam" absent by design set a proxy/connector mints a short-lived IAM token; never expect a DSN

If running locally or in CI using the Cloud SQL Auth Proxy sidecar, the proxy listens at 127.0.0.1:5432 and handles authentication under the hood.

fluffy-chainsaw.yaml

defaults:
  databases:
    instance: app-db          # bind every database to one server; override per-db to split

databases:
  users:
    extensions: [pgcrypto]    # string list
  orders: {}                  # all fields optional except the instance binding

instances:                    # operator-declared substrate — no code marker
  app-db:
    tier: db-custom-1-3840    # all optional; strong defaults fill the rest
    database_version: POSTGRES_16
    availability_type: REGIONAL   # ZONAL | REGIONAL
    # existing: true          # reference a server you already run, don't provision

Field reference (anything else fails validation):

A runner gets access by listing the database under uses.databases — see runners.md.

Using a database another app owns

existing: true on a database marks it owned elsewhere: it is looked up at deploy (fails loudly if missing) and granted onto, but never created, altered, or dropped by this app. Same for an existing: true instance — the first app to declare it provisions the server; every other app sets existing: true to reference it without taking ownership. Destroying a referencing app never touches the shared instance.

Schema changes

Don't run DDL from app code. Declare databases.<name>.migrations.run and let fluffy-chainsaw migrate apply your migrations as a dedicated migrator identity — the runner identity serving traffic then holds no DDL rights at all. Full guide: migrations.md.

Gotchas