Skip to main content
Alpha

Database & migration

Goal: take a freshly scaffolded NetScript workspace and stand up its database — bring up the AppHost, create the first migration, generate the typed Prisma client, seed it, and confirm the schema is applied — using only the public netscript db commands.

This recipe uses Postgres, the recommended default and the engine every tutorial builds on. The database is polyglot, though: NetScript scaffolds a Prisma-backed database for four engines, chosen at scaffold time with --dbnetscript init my-app --db postgres|mysql|mssql|sqlite. Postgres, MySQL, and MSSQL come up as Aspire container resources; SQLite is file-backed and needs no container. The migration loop below is identical on every engine; only the provisioning differs.

This is a task-oriented recipe. The single most important fact, and the one most people trip over: netscript db commands provision and talk to Postgres through Aspire. Aspire is step 2, not an afterthought. You bring the database up first with cd aspire && aspire start, then the db commands have something to connect to. Skip that ordering and db init fails with aspire start failed before it ever reaches Prisma. Everything below assumes the default Aspire layout; the --no-aspire escape hatch is called out where it differs.

What you'll end up with

By the end of this recipe you will have a running Postgres container, a first migration on disk and applied, a generated Prisma client (with matching zod schemas) under database/postgres/schema/.generated/, seeded baseline rows, and a green netscript db status. The whole loop is four commands, run once, against a database that Aspire is keeping alive in a second terminal.

The migration workflow at a glance
NameTypeDescription
aspire start from aspire/ Provisions the Postgres + Redis containers. Must be up first and stay running.
netscript db init --name init from workspace root Initializes tooling, creates the first migration from schema.prisma, applies it to Postgres.
netscript db generate from workspace root Generates the Deno-runtime Prisma client + zod schemas into .generated. Re-run after every schema edit.
netscript db seed from workspace root Runs database/postgres/scripts/seed.ts to populate baseline rows.
netscript db status from workspace root Reports which migrations are applied and whether the database is in sync — the authoritative verification.

Before you start

Prerequisites
NameTypeDescription
NetScript workspace netscript init … A workspace scaffolded with a database. This recipe uses --db postgres (the recommended default), which lays down database/postgres/ with a Prisma schema, prisma.config.ts, and seed scripts. Swap --db postgres for mysql, mssql, or sqlite for a different engine — the workflow is the same, only the database// directory differs.
netscript CLI on PATH Install with deno install --global --allow-all --name netscript jsr:@netscript/cli@0.0.1-alpha.12 — then netscript --help should print.
Aspire CLI aspire Used to provision Postgres and Redis as containers via Docker. Confirm with aspire --version. Docker/Podman must be running.
Deno 2.x deno --version. Prisma generation runs under the Deno runtime (the schema sets runtime="deno").

If you scaffolded with --no-aspire, you are responsible for pointing the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL (see Deploy); there is no AppHost to start, so you skip Step 2, but the migration commands in Step 3 are otherwise identical and talk to whatever database those variables resolve to.

Run every command from your workspace root unless a step says otherwise. To target a workspace other than the current directory, pass --project-root <path> — useful in CI or when scripting against a checkout you are not cd-ed into.

Step 1 — Confirm the database wiring

A scaffolded workspace already contains the database workspace at database/postgres/. You do not author any of this by hand; the point of this step is to recognize the moving parts so the later commands make sense:

my-app/
├── appsettings.json              # NetScript.Databases.postgres — Engine/Mode/DatabaseName
├── netscript.config.ts           # databases.config is intentionally empty here
└── database/postgres/
    ├── schema/
    │   ├── schema.prisma          # root: generator client (runtime="deno"), generator zod, datasource db
    │   ├── plugins/               # per-plugin .prisma models aggregated here
    │   │   ├── workers/workers.prisma
    │   │   ├── sagas/sagas.prisma
    │   │   └── triggers/triggers.prisma
    │   └── .generated/            # created by `db generate`: Prisma client + zod
    ├── prisma.config.ts           # schema dir + migrations path + datasource url
    ├── migrations/                # created by `db init`: timestamped migration dirs
    └── scripts/                   # migrate.ts, seed.ts, generate-zod.ts, …

Two things worth internalizing before you run anything:

  • The datasource is Postgres, driven by appsettings.json — not by netscript.config.ts. In the scaffold, netscript.config.ts has databases: { config: [] } (empty). The real connection details live in appsettings.json under NetScript.Databases.postgres (Engine: "Postgres", Mode: "Container", a generated DatabaseName, and a persistent DataPath of .data/postgres). Aspire reads that block to provision the container, and prisma.config.ts resolves its datasource URL from the same source. If you ever wonder "where does the connection string come from?", the answer is this block, not the config file.
  • Plugin schemas are split out and aggregated. Each first-party plugin ships its own database/<plugin>.prisma; those are aggregated under database/postgres/schema/plugins/<plugin>/. So after adding the workers, sagas, and triggers plugins you'll see their tables (e.g. model JobDefinition from workers) join the root ExampleRecord model in the same Postgres datasource. One migration covers them all.

Step 2 — Bring up Postgres with Aspire (do this first)

From the workspace root, change into the isolated Aspire folder and start the AppHost. The TypeScript AppHost (aspire/apphost.mts) lives in aspire/ to keep its Node graph separate from the Deno workspace, so the commands run from there:

cd aspire
aspire restore   # one-time: downloads the AppHost SDK modules
aspire start       # provisions Postgres + Redis containers and starts the resource graph

Leave aspire start running in this terminal. It provisions both the Postgres database and the Redis cache (your KV/queue backend) as Docker containers — no manual docker run and no local Postgres install required. When it settles you'll have:

  • The Aspire dashboard at http://localhost:18888 — the access token is printed in the aspire start output. Open it and confirm the postgres and redis resources are green.
  • Resources named postgres, redis, and the per-capability services/processors (workers-api, workers, sagas-api, sagas, triggers-api, triggers). If you also scaffolded auth and streams, you'll see auth-api (:8094) and the streams service (:4437) join the graph.

Step 3 — Run the migration workflow

Open a second terminal at the workspace root (keep aspire start going in the first). Now Postgres is reachable, run the four-command database workflow in order:

# Create the first migration from the current Prisma schema and apply it.
# --name labels the migration directory (here: a migration called "init").
netscript db init --name init
# Generate the Deno-runtime Prisma client (and the zod schemas) into
# database/postgres/schema/.generated so your code can import typed models.
netscript db generate
# Run the workspace seed scripts (database/postgres/scripts/seed.ts)
# to populate baseline rows.
netscript db seed
# Show migration / tooling status: which migrations are applied and
# whether the database is in sync with the schema.
netscript db status

What each step does, in plain terms:

  • netscript db init --name init — initializes database tooling and creates the first migration from database/postgres/schema/schema.prisma, writing it to the migrations path declared in prisma.config.ts (migrations: { path: 'migrations' }) and applying it to the running Postgres container. The --name value names the migration directory; pick something descriptive for later migrations (add-orders, index-email), not just init.
  • netscript db generate — runs database code generation: the Prisma client (generator client with runtime="deno", output ./.generated) plus the zod schemas (generator zod./.generated/zod). Re-run this whenever the schema changes, because the generated types are how your application code stays in lockstep with the database.
  • netscript db seed — executes the seed scripts under database/postgres/scripts/. Seeds are ordinary Deno scripts, so they can insert baseline rows, reference data, or a first admin record using the freshly generated client.
  • netscript db status — reports migration/tooling status so you can confirm the schema is applied and in sync. This is the command you'll reach for most often once you start iterating.

Step 4 — Verify

Confirm the database is migrated and seeded three ways, from most to least authoritative:

  1. CLI status. netscript db status reports the init migration as applied and the schema in sync. This is the authoritative check — if it is green, the rest is corroboration.
  2. Generated client exists. database/postgres/schema/.generated/ now contains the Prisma client and the zod schemas — proof db generate succeeded and your typed models are importable from application code.
  3. Aspire dashboard. The postgres resource at http://localhost:18888 is green and shows recent activity from the migration and seed runs. Use it to eyeball that the container is healthy.

If all three line up, your database is provisioned, migrated, and seeded — you can now import the generated client in a service or worker and read the rows the seed wrote.

Where the data goes next

Once the schema is live, the generated client is what every other capability reads and writes through. Queue jobs persist their message_queue rows (the PostgreSQL queue backend shares this same datasource), durable sagas persist runtime state when configured with the Prisma store backend, and your services query their models directly. The migration loop you just ran is the foundation the rest of the workspace stands on.

See also

Reference — database

The full generated @netscript/database API: schema helpers, the Prisma adapter surface, and migration tooling.

Capability — Database & Prisma

How NetScript wires Prisma 7 + Postgres, per-plugin schema aggregation, and the appsettings-driven datasource.

Orchestration with Aspire

Why the AppHost (aspire/apphost.mts) provisions Postgres and Redis, and how the resource graph fits together.

Queue / KV / cron

The next recipe: use the KV and queue backends — including the PostgreSQL queue provider that shares this datasource.

Deploy without Aspire

Point the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL using the --no-aspire escape hatch.

For the MySQL adapter surface, see prisma-adapter-mysql.