---
name: sveltekit-postgres-drizzle
description: "Use when building, refactoring, or reviewing a SvelteKit + PostgreSQL + Drizzle ORM project (SvelteKit, Svelte, PostgreSQL, Supabase, Drizzle, TypeScript, Tailwind CSS). Production guidelines for SvelteKit form actions and load functions, Drizzle ORM over PostgreSQL, and Svelte 5 runes-based reactivity."
license: MIT
metadata:
  source: https://stackitfast.com/rules/sveltekit-postgres-drizzle
  version: "2026-09-10"
---

# SvelteKit + PostgreSQL + Drizzle ORM — Agent Skill

## When to use this skill
- Any task that scaffolds, modifies, refactors, or reviews code in a SvelteKit + PostgreSQL + Drizzle ORM codebase.
- Whenever the project depends on SvelteKit, Svelte, PostgreSQL, Supabase, Drizzle, TypeScript, Tailwind CSS.
- Apply these guidelines before proposing architecture, database, or deployment changes.

## Guidelines
# Project Architecture & Guidelines (SvelteKit + PostgreSQL + Drizzle)

## 1. System Architecture
- **Framework**: SvelteKit (Svelte 5, runes-based reactivity `$state`/`$derived`/`$effect`).
- **Database & ORM**: PostgreSQL with Drizzle ORM (`drizzle-orm/postgres-js`, `drizzle-kit`) — for solo builders and small teams, point `DATABASE_URL` at a managed provider (Supabase or Neon, both have a usable free tier) instead of operating your own Postgres instance; self-host only once you need full infra control.
- **Rendering**: Server-side rendering by default; `+page.server.ts` for data loading, form actions for mutations.
- **Validation**: TypeScript strict mode + Zod for form and API input validation.

## 2. Load Functions & Data Fetching
- Fetch data exclusively in `+page.server.ts` / `+layout.server.ts` `load()` functions — never in `onMount` for data the page needs to render.
- Return typed data from `load()`; SvelteKit infers `PageData`/`LayoutData` automatically via `./$types`.
- Use `depends()`/`invalidate()` for fine-grained cache invalidation instead of full page reloads after mutations.

## 3. Form Actions & Mutations
- All writes go through named form actions in `+page.server.ts` (`export const actions = { create: async ({ request }) => {...} }`), not client-side `fetch` to API routes, unless the mutation is triggered outside a `<form>`.
- Validate `await request.formData()` with Zod before touching the database; return `fail(400, { errors })` on validation failure so the form can show inline errors without JavaScript.
- Use progressive enhancement (`use:enhance`) so forms work with JS disabled and get optimistic UI when enabled.

## 4. Database Client & Migrations
- Singleton Drizzle client in `src/lib/server/db/index.ts`, imported only from `+page.server.ts` / `+server.ts` files — never from `.svelte` components (build fails loudly if `postgres` leaks into the client bundle, which is the intended guardrail).
  ```typescript
  import { drizzle } from 'drizzle-orm/postgres-js';
  import postgres from 'postgres';
  import { DATABASE_URL } from '$env/static/private';
  import * as schema from './schema';

  // DATABASE_URL: Supabase/Neon pooled connection string for solo/small-team
  // deploys — skips operating a Postgres server entirely.
  const client = postgres(DATABASE_URL, { prepare: false });
  export const db = drizzle(client, { schema });
  ```
- Generate migrations with `drizzle-kit generate`; apply with `drizzle-kit migrate`. Never hand-edit generated SQL.

## 5. Common Pitfalls / Coding Standards
- ❌ Importing `$lib/server/*` modules from client-reachable code — SvelteKit throws at build time, but only if the import chain is direct; re-exporting through a shared barrel file can hide the leak.
- ❌ Fetching in `onMount` for data needed on first paint, causing layout shift and losing SSR benefits.
- ✅ Use `$state`/`$derived` runes for component-local reactivity; avoid the legacy `$:` reactive statement style in new code.

## 6. Testing Conventions
- Vitest for unit tests on `load()` functions and Drizzle query helpers, with a `pg` test container or Supabase local dev instance.
- Playwright for e2e coverage of form actions, including the no-JS progressive-enhancement path.
- Run `svelte-check` and `tsc --noEmit` as required checks — Svelte's type-checking catches prop/slot mismatches that plain `tsc` misses.

## 7. Git Workflow & PR Conventions
- Conventional Commits scoped to the route, e.g. `fix(routes/projects): validate slug uniqueness in create action`.
- Generated Drizzle migrations ship in the same PR as the schema change.
- Require `svelte-check`, `tsc --noEmit`, and `bun run build` green before merge.
- Squash-merge; never `drizzle-kit push` against a shared or production database from a feature branch.

## Source
Maintained at https://stackitfast.com/rules/sveltekit-postgres-drizzle — also available as AGENTS.md, CLAUDE.md, and Cursor .mdc.