Skip to content

⚙️ A full stack web application example using my current favorite tech stack. AI-ready. Subject to change.

License

Notifications You must be signed in to change notification settings

statico/ian-stack-2025

Repository files navigation

Ian's Stack (2025 Edition)

GitHub Actions Workflow Status GitHub Actions Workflow Status GitHub License

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.

Screenshot of this TODO app

Goals

  • 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

Tech Stack

  • 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.

Setup

You'll need to install Postgres. I suggest Docker or Postgres.app on macOS. Then:

  1. Create a .env file in the root directory and set the DATABASE_URL to postgresql://localhost:5432 or wherever your Postgres server is running.
  2. Get Node.js 24+ and pnpm installed.
  3. Run pnpm install to install dependencies
  4. Run pnpm db:latest to initialize the database
  5. Run pnpm dev to start the development server
  6. Visit http://localhost:3000 to see the app

Directory Structure

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

Key Directories

  • src/app/ - Next.js app router pages and API routes
  • src/components/ - Reusable React components, including shadcn/ui components
  • src/lib/ - Utility functions and client-side libraries
  • src/server/ - Server-side code including database models, tRPC routers, and business logic
  • src/server/db/ - Database configuration, migrations, and generated types
  • src/server/trpc/ - tRPC API route definitions and server setup
  • src/test/ - Test utilities and setup configuration

Common Tasks

Creating a database migration

Create a new Kysely migration file in src/server/db/migrations/. Then run:

  1. pnpm db:latest to apply the migration
  2. pnpm db:codegen to generate the Kysely types
  3. pnpm db:schema to export the database schema to docs/schema.sql (this helps AI tools)

Consider also pnpm db:down to undo the migration

Running tests

Simply run pnpm test

Preferred Ways to Extend

  • 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 userProcedure that 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/mdx makes it easy to embed documentation in a site using MDX
  • Blogging - Use next-mdx-remote with smartypants, grey-matter and the rss module 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.

Things to Avoid

  • Modules with a large dependency tree - prefer NPM modules with zero or very few dependencies
  • lodash which is mostly unnecessary with TypeScript, though es-toolkit has some useful functions
  • Emotion and Styled Components, which reduce performance as apps get larger
  • NextAuth.js is messy and difficult to use (sorry)

License

MIT License