Skip to content
Docs just relaunched - explore the new sidebar, OG images, and AI-ready content.
Build With SyntaxKit

Database

Prisma schema, migrations, and seeding on PostgreSQL.

Last updated on

4 min read

SyntaxKit uses PostgreSQL via Prisma, owned by packages/database. Bring any Postgres you like: a hosted provider such as Neon, Supabase, or RDS, or one you run yourself with Docker. The same DATABASE_URL switches the app between them, and the Prisma client picks the right driver adapter automatically.

Choose A Postgres

Create a Postgres database on Neon, Supabase, AWS RDS, or any other Postgres provider, then paste the connection string into DATABASE_URL:

DATABASE_URL="postgresql://<user>:<password>@<host>/<database>"

*.neon.tech URLs automatically use the @prisma/adapter-neon serverless adapter (HTTP-friendly, no socket pooling). Every other host falls back to the standard @prisma/adapter-pg driver. Your application code is identical either way.

For local development, the simplest path is a one-off Postgres container:

docker run --name syntaxkit-postgres \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=syntaxkit \
  -p 5432:5432 \
  -d postgres:16

Then point DATABASE_URL at it:

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/syntaxkit"

For production self-hosting, the docker-compose.yml at the repo root does not bundle a Postgres service. Provide a DATABASE_URL to a managed instance (Neon, Supabase, RDS, ...) or a separate self-hosted Postgres, then run the bundled migrator service to apply schema migrations before bringing up web.

Schema Layout

The Prisma schema is split across several files in packages/database/prisma/ so each domain has a clear home:

schema.prismaGenerator and datasource config; everything else loads from siblings
auth.generated.prismaAuto-generated by pnpm auth:generate; do not hand-edit
billing.prismaSubscription, StripeWebhookEvent
ai.prismaChat, Message, AiUsageEvent
effects.prismaOutboundEffect for side-effect idempotency
migrations/Versioned SQL migrations applied by Prisma
seed.tsMulti-mode seed runner: bootstrap, demo, test

What's Modeled

How The Models Connect

The diagram below shows the foreign-key relationships across the four domains.

Entity relationship diagram of the SyntaxKit data model

Standalone tables (Verification, StripeWebhookEvent, OutboundEffect) are intentionally omitted because they are written and read by name, not by relation.

The diagram source lives at apps/docs/diagrams/database-erd.mmd. Rerun pnpm --filter @syntaxkit/docs diagrams:build after editing it to refresh both SVG variants.

Two patterns are worth calling out: every product entity (Subscription, Chat, AiUsageEvent) is scoped to an Organization rather than a User, and User is wired into orgs only through Member. That is what makes SyntaxKit multi-tenant by default.

Common Workflows

Expand the task you're doing.

Add or change a model

Edit the right schema in packages/database/prisma/models/, then create a migration:

pnpm db:migrate:dev

Prisma prompts for a name; pick something descriptive (add_team_invitations, not update).

Apply pending migrations

On a fresh checkout, run:

pnpm db:migrate:dev

This applies every migration in prisma/migrations/ and regenerates the typed client. The :dev suffix matches prisma migrate dev and is intended for development only; production deploys use pnpm db:migrate:deploy (see Migrations In Production).

Reset and reseed locally

Wipe the local database and start clean:

pnpm db:reset

Or wipe and load demo data in one shot:

pnpm db:reset:demo
Inspect data

Open the Prisma Studio GUI:

pnpm db:studio
Regenerate the typed client
pnpm db:generate

pnpm install no longer triggers prisma generate. Run pnpm db:generate after any hand-edit to a *.prisma file or a fresh clone, before pnpm dev.

Regenerate Better Auth-owned models
pnpm auth:generate

Never edit auth.generated.prisma by hand. Update packages/auth/src/server.ts instead and rerun this script.

Using The Client In Your Code

Import the singleton client from @syntaxkit/database and write Prisma queries the usual way:

import { prisma } from "@syntaxkit/database";

const orgs = await prisma.organization.findMany({
  where: { members: { some: { userId } } },
});

prisma is a process-wide singleton. The driver adapter is chosen for you based on DATABASE_URL (Neon serverless when the host ends with .neon.tech, standard pg everywhere else), so your application code never has to know which Postgres it is talking to.

Migrations In Production

Production deploys use the non-interactive migrator:

pnpm db:migrate:deploy

Two important details:

  • The migrator service in docker-compose.yml runs this command for self-hosted Docker deploys. Run it before bringing up web after every schema change.
  • pnpm db:migrate:status reports whether the database is in sync with the migrations directory; useful in CI and on deploy targets that do not auto-migrate.

For platform-specific runbooks (Vercel, Fly.io, Render, Docker), see Deployment and Going To Production.

Seeding

prisma db seed runs through packages/database/prisma/seed.ts, which dispatches to one of three modes:

bootstrap

Default. Leaves the database clean so first sign-up creates the personal organization. Run with pnpm db:seed:bootstrap or just pnpm db:seed.

demo

Sample organizations and an admin (admin@demo.syntaxkit.com / password123). Run with pnpm db:seed:demo.

test

Fixtures used by pnpm test:integration. Run with pnpm db:seed:test.

Where To Go Next

Was this page helpful?

On this page