Skip to content
GitHub

Architecture Overview

LayerTechnologyPurpose
FrontendNext.js 15 App RouterReact 19, SSR, RSC
AuthCustom JWTUser management
DatabaseNeon + Drizzle ORMServerless Postgres + type-safe queries
PaymentsStripeSubscriptions + checkout
EmailResendTransactional emails
ValidationZodRuntime validation
Data FetchingTanStack QueryServer state + caching
FormsReact Hook FormForm management
UIshadcn/ui + TailwindComponents + styling
AnalyticsPostHog + AxiomEvents + logging
JobsTrigger.devBackground tasks
MonorepoTurborepo + pnpmBuild + packages

Creating a task:

User fills form

Zod validates (client)

Submit to API

Zod validates (server)

Drizzle inserts

TanStack Query updates cache

UI updates

Fetching tasks:

Component calls useTasks()

TanStack Query checks cache

If stale, fetch from API

JWT auth verification

Drizzle queries Neon

Cache result

Render
Drizzle Schema (source of truth)

TypeScript Types + Zod Schemas (auto-generated with Drizzle-Zod)

@workspace/database exports entities + schemas

@workspace/types re-exports + composes API responses

API + Frontend import ONLY from @workspace/types

Example:

// 1. Define in Drizzle
export const tasks = pgTable("tasks", { id: integer(), title: varchar() });

// 2. Auto-generated with Drizzle Zod
export type Task = typeof tasks.$inferSelect;
export const insertTaskSchema = createInsertSchema(tasks);

// 3. Use everywhere
const validated = insertTaskSchema.parse(body); // API
const form = useForm({ resolver: zodResolver(insertTaskSchema) }); // Frontend

See Type System and Type Flow for details.

  1. Database schema = source of truth - Everything derives from it
  2. Validate twice - Client (UX) + server (security)
  3. Type-safe everywhere - @workspace/types ensures API/frontend alignment
  4. Cache by default - TanStack Query handles it
  5. Monorepo - Share code, isolate apps

Deployment Guide · Package Docs