Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

statico/ian-stack-2025

Open more actions menu

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 oxlint and Prettier 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)
  • oxlint - Fast Rust-based linter for JavaScript and TypeScript
  • Prettier - Opinionated code formatter
  • 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.

Code quality note: This project uses oxlint for linting and Prettier for formatting. oxlint is a fast Rust-based linter that's significantly faster than ESLint.

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

Code Quality

This project uses oxlint for linting and Prettier for formatting:

  • pnpm everything - Run all checks (lint, format, types, tests) with automatic fixes
  • pnpm lint - Check for linting issues
  • pnpm lint:fix - Automatically fix linting issues
  • pnpm format - Check code formatting
  • pnpm format:fix - Automatically fix formatting issues

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

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.