golemkit

Core

Architecture

One rule explains the whole repository: thin routes, thick modules. Files under src/app/ only wire HTTP and UI to business logic; the logic itself lives in feature modules under src/modules/. This is what makes the codebase predictable for both humans and coding agents.

The module shape

Every feature module follows the same anatomy — src/modules/auth/ is the canonical example to copy:

src/modules/<feature>/
├── schema.ts          # Drizzle tables this module owns
├── <feature>.ts       # server logic: typed, JSDoc'd functions
├── <feature>-client.ts# only if the browser needs an API
├── components/        # feature-specific React components
└── index.ts           # the public server API (barrel)

Server code imports across modules through the barrel (@/modules/billing); client components import -client files directly. Modules in the box: auth, ai, billing, email, ratelimit, rag, blog, docs, admin.

Database workflow

  1. Define tables in the module's schema.ts.
  2. Re-export them from src/db/schema.ts.
  3. pnpm db:generate — writes a SQL migration to drizzle/ (works offline).
  4. pnpm db:migrate — applies it.

Columns are snake_case in Postgres, camelCase in TypeScript.

Environment

src/lib/env.ts declares and zod-validates every variable at startup — server-only, never import it into client code. New variable? Add it to the schema there and to .env.example with a placeholder.

The Next.js 16 gotcha

Middleware is now src/proxy.ts (exporting proxy(request)). It does an optimistic cookie check only; real session validation happens in the dashboard layout via requireSession(). When touching unfamiliar App Router APIs, the current docs ship inside node_modules/next/dist/docs/.

Conventions worth knowing

  • TypeScript strict plus noUncheckedIndexedAccess; no any anywhere.
  • Every exported function carries a JSDoc block.
  • Files are kebab-case; components PascalCase; DB snake_case.
  • After changes: pnpm typecheck && pnpm lint, plus pnpm build for route/RSC changes.