A full stack web application example using my current favorite tech stack.
Clone it with your favorite AI-assisted coding tool to get started quickly. Send complaints and nitpicks via Bluesky and Mastodon.
- Flexible - It should be easy and quick to add functionality
- Strong typing - Types should be available at all layers, frontend and backend
- Best practices - Use Prettier and ESLint to automatically enforce code uniformity
- Testable - The app should be testable with unit tests and E2E browser tests
- Maintainable - Use migrations for database changes and minimize dependencies
- Updateable - Keeping the dependencies up to date should be easy
- GitHub-ready - Integrates with GitHub Actions for CI/CD
- Dockerizable - The entire app should fit nicely inside a Docker container
- AI-ready - AI coding agents should be able to modify the codebase with ease
- TypeScript - A decent typed language that can be used on both frontend and backend
- React - A great frontend framework when used responsibly with a strong ecosystem
- Next.js - Still the best option to get a full-stack React app -- but I have strong reservations about its complexity
- tRPC with Tanstack Query for end-to-end typesafe APIs, which is less error-prone than REST APIs and less of a headache than React Server Components, plus it does streaming responses!
- PostgreSQL - The best choice for a general-purpose OLTP database
- PGLite - For an in-memory PostgreSQL database for testing
- Kysely - Type-safe SQL query builder and database migration tool
- shadcn/ui and Tailwind CSS v4 - Solid UI choices with shadcn's smart vendored approach means performance plus maintainability
- pnpm - A fast and reliable package manager
- Vitest - Super fast unit testing
- Playwright - E2E browser testing (not currently included because I can't get it to work with PGLite)
- Prettier and ESLint - Automatic code formatting and linting on save (in VS Code and Cursor) and on commit (via Husky)
- AGENTS.md - "A simple, open format for guiding coding agents"
Next.js note: I'm on the lookout for a Next.js replacement due to its increasing complexity and size. Astro is wonderful for static sites but requires more setup for dynamic sites.
Prettier and ESLint note: I'm strongly considering Oxc and/or Biome to replace Prettier and ESLint. ESLint is slow and difficult to maintain. Fast, single-executable tools are probably the right direction for TypeScript tooling.
You'll need to install Postgres. I suggest Docker or Postgres.app on macOS. Then:
- Create a
.envfile in the root directory and set theDATABASE_URLtopostgresql://localhost:5432or wherever your Postgres server is running. - Get
Node.js 24+and pnpm installed. - Run
pnpm installto install dependencies - Run
pnpm db:latestto initialize the database - Run
pnpm devto start the development server - Visit http://localhost:3000 to see the app
ian-stack-2025/
├── src/
│ ├── app/ # Next.js app router pages
│ │ ├── api/trpc/[trpc]/route.ts # tRPC API handler
│ │ ├── globals.css # Global styles (part of shadcn/ui)
│ │ ├── layout.tsx # Root layout component
│ │ └── page.tsx # Home page
│ ├── components/ # Reusable UI components
│ │ ├── theme-provider.tsx # Theme context provider
│ │ └── ui/ # shadcn/ui components
│ ├── lib/ # Utility libraries for frontend and sometimes backend
│ │ ├── trpc/ # tRPC client setup
│ ├── server/ # Server-side code
│ │ ├── db/ # Database configuration
│ │ │ ├── index.ts # Database connection
│ │ │ ├── migrate.ts # Migration runner
│ │ │ ├── migrations/ # Database migrations
│ │ │ └── types.ts # Generated Kysely types from pnpm db:codegen
│ │ ├── models/ # Database models
│ │ │ ├── todos.ts # Todo model
│ │ │ └── todos.test.ts # Todo model tests
│ │ ├── trpc/ # tRPC server setup
│ │ │ ├── routers/ # API route handlers
│ │ │ │ ├── index.ts # Main router
│ │ │ │ └── todos.ts # Todo routes
│ │ │ └── trpc.ts # tRPC configuration
│ │ ├── env.ts # Environment var helper
│ │ └── logging.ts # Simple logging utility
│ └── test/ # Test utilities
├── docs/ # Documentation
│ └── schema.sql # Database schema export from pnpm db:schema
├── public/ # Static assets
├── patches/ # Package patches made with `pnpm patch`
├── AGENTS.md # AI agent guidelines
├── CLAUDE.md # Symlink to AGENTS.md for Claude Code
├── components.json # shadcn/ui configuration
├── Dockerfile # Container configuration
src/app/- Next.js app router pages and API routessrc/components/- Reusable React components, including shadcn/ui componentssrc/lib/- Utility functions and client-side librariessrc/server/- Server-side code including database models, tRPC routers, and business logicsrc/server/db/- Database configuration, migrations, and generated typessrc/server/trpc/- tRPC API route definitions and server setupsrc/test/- Test utilities and setup configuration
Create a new Kysely migration file in src/server/db/migrations/. Then run:
pnpm db:latestto apply the migrationpnpm db:codegento generate the Kysely typespnpm db:schemato export the database schema todocs/schema.sql(this helps AI tools)
Consider also pnpm db:down to undo the migration
Simply run pnpm test
- Authentication - Clerk is fantastic with a generous free tier.
- Authorization - Use Clerk public metadata to add custom user roles and permissions, and extend the tRPC server with a
userProcedurethat performs checks - Postgres extensions - Check out PostGIS for geography functions, pgvector for vector search, and TimescaleDB for storing event data in columnar format
- Playwright - Use Playwright for E2E browser testing
- AI - BAML is a fantastic way to manage AI prompts and models with type safety and structured output
- Documentation - The
@next/mdxmakes it easy to embed documentation in a site using MDX - Blogging - Use
next-mdx-remotewithsmartypants,grey-matterand therssmodule to generate a blog - Queues - Consider pg-boss and a separate worker script (run with
tsx) for a lightweight background processing solution, though larger workloads will require something more robust - Client-side global state - Consider Zustand for any client-size global state management needs, like filter views
- Mocking external services - Use MSW for mocking external services in tests
- Type-safe pattern matching - Use ts-pattern for type-safe pattern matching in TypeScript
- ORMs - I'm not a big fan of ORMs and find query builders to be far superior. But if I had to choose one, I'd probably go with Prisma.
- Modules with a large dependency tree - prefer NPM modules with zero or very few dependencies
lodashwhich is mostly unnecessary with TypeScript, thoughes-toolkithas some useful functions- Emotion and Styled Components, which reduce performance as apps get larger
- NextAuth.js is messy and difficult to use (sorry)