This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
devenv up # Start everything (runs setup, then PostgreSQL, dev server, Storybook)
just # Run check and test (default)
just dev # Start Express server on port 3000
just check # TypeScript type checking
just test # Run vitest unit tests + Playwright e2e tests
just fmt # Format code with treefmt
just storybook # Run Storybook on port 6006
just psql # Connect to development database
just db-migration # Generate Drizzle migrations
just db-migrate # Run Drizzle migrationsNote: just test runs both unit tests (vitest) and e2e tests (Playwright). Do not run vitest separately before just test or you will run unit tests twice.
React Router 7 full-stack application with Express.js backend and PostgreSQL database.
Request flow:
server.jscreates Express app with Vite middleware (dev) or static assets (prod)server/app.tsapplies security middleware and wraps requests inDatabaseContext- Route loaders/actions call
database()from~/database/contextto access Drizzle ORM app/root.tsxprovides the root layout with i18n provider and error boundary
Key patterns:
- Database context:
AsyncLocalStorageprovides thread-safe database access in SSR. Always usedatabase()from~/database/contextin loaders/actions. - Route typing: React Router generates types in
+types/directories. ImportRoutetypes for loader/action/component props. - i18n: Translations in
app/i18n/en.ts. Never hardcode user-facing strings. UseuseTranslation()hook in components; useconst t = i18n.t.bind(i18n)in meta functions and error boundaries.
Directory structure:
app/routes/- React Router routes with loaders/actionsserver/middleware/- Express middleware (CSRF, security headers)database/- Drizzle schema and context
- React 19, React Router 7, TypeScript, Tailwind CSS 4
- Express 5, Drizzle ORM, PostgreSQL 17 (with pgvector, PostGIS)
- Playwright for e2e tests, Storybook for component development
- pnpm for packages, devenv/Nix for development environment
- Small, focused tests that verify one behavior each
- Clear separation between setup and expectations (Given/When/Then)
- Referential transparency: tests must not depend on environment, timezone, or implicit defaults
- Lightweight dependency injection over DI frameworks; pass dependencies as parameters
- Assert on behavior and outcomes, not implementation details
- Only test implementation when critically important (security, privacy)
- Never write tests that compare two unknowns (e.g., "locale A != locale B" proves nothing)
- Atomic commits: one logical change per commit
- Short imperative summary (e.g., "Add date formatter", not "Added date formatting utility using Intl.DateTimeFormat")
- Longer explanation in the body if needed
- No "Generated with Claude Code" or "Co-Authored-By" lines