Documentation
Documentation
Introduction

Getting Started

Getting StartedInstallationQuick StartProject Structure

Architecture

Architecture OverviewTech StacktRPC MiddlewareDesign Principles

Patterns

Code Patterns & ConventionsFeature ModulesError HandlingType Safety

Database

DatabaseSchema DefinitionDatabase OperationsMigrationsCaching

API

tRPCProceduresRouterstRPC Proxy Setup
APIsOpenAPIREST Endpoints

Auth & Access

AuthenticationConfigurationOAuth ProvidersRolesSession Management
AuthorizationUser RolesPermissions

Routing & i18n

RoutingDeclarative RoutingNavigation
InternationalizationTranslationsLocale Routing

Components & UI

ComponentsButtonsFormsNavigationDialogs
StylesTailwind CSSThemingTypography

Storage

StorageConfigurationUsageBuckets

Configuration

ConfigurationEnvironment VariablesFeature Flags

Templates

Template GuidesCreate New FeatureCreate New PageCreate Database TableCreate tRPC RouterAdd Translations

Development

DevelopmentCommandsAI AgentsBest Practices

Tech Stack

Complete overview of technologies used and why we chose them

Overview

This project uses a carefully selected set of modern technologies that work together to provide a robust, type-safe, and performant full-stack application.

Core Technologies

Framework: Next.js 16+

Next.js is our primary framework, using the App Router for server-first architecture.

Why Next.js?

  • Server Components - Improved performance and reduced client bundle size
  • Built-in optimization - Automatic code splitting, image optimization, font optimization
  • Flexible rendering - Static, dynamic, and incremental static regeneration
  • File-based routing - Intuitive project structure
  • API routes - Backend and frontend in one codebase
  • Strong ecosystem - Extensive tooling and community support

Key Features Used:

  • App Router for modern routing patterns
  • Server Actions for form submissions (optional)
  • Streaming for progressive loading
  • unstable_cache for server-side caching
  • Middleware for request interception
// Example: Server Component with data fetching
import { api } from "@/trpc/server";

export default async function UsersPage() {
  const users = await api.users.list.query({ limit: 10 });
  
  return (
    <div>
      {users.map((user) => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

Language: TypeScript

TypeScript provides static typing for JavaScript, catching errors at compile-time.

Why TypeScript?

  • Type safety - Catch errors before runtime
  • Better IDE support - Autocomplete, refactoring, navigation
  • Self-documenting code - Types serve as inline documentation
  • Refactoring confidence - Know exactly what breaks when you change code
  • Team scaling - Enforces contracts between code modules

Configuration:

  • Strict mode enabled (strict: true)
  • Path aliases configured (@/ for src/)
  • Module resolution set to bundler
// Example: Type-safe function
type User = {
  id: string;
  name: string;
  email: string;
};

export function formatUserName(user: User): string {
  return user.name.toUpperCase();
  // TypeScript ensures 'user' has 'name' property
}

Database: PostgreSQL + Drizzle ORM

PostgreSQL is our relational database, accessed through Drizzle ORM.

Why PostgreSQL?

  • Reliability - ACID compliance, proven track record
  • Feature-rich - JSON support, full-text search, GIS extensions
  • Performance - Efficient indexing and query optimization
  • Scalability - Handles large datasets and concurrent connections
  • Open source - No licensing costs

Why Drizzle ORM?

  • Type-safe queries - Full TypeScript integration
  • Zero runtime overhead - No query builders at runtime
  • SQL-like syntax - Easy to learn if you know SQL
  • Migration support - Version control for your database schema
  • Edge compatible - Works with serverless databases like Neon
// Example: Type-safe query with Drizzle
import { db } from "@/db";
import { users } from "@/db/tables/users";
import { eq } from "drizzle-orm";

const user = await db.query.users.findFirst({
  where: eq(users.email, "john@example.com"),
  with: {
    organizations: true,
  },
});

See the Database guide for more details.

API: tRPC

tRPC provides end-to-end type safety for our API layer without code generation.

Why tRPC?

  • Type inference - Share types between client and server automatically
  • No code generation - Types flow naturally from implementation
  • Developer experience - Autocomplete everywhere, catch errors instantly
  • React Query integration - Powerful data fetching and caching
  • Lightweight - Small bundle size, minimal overhead

Alternative to REST/GraphQL:

  • No need to write OpenAPI specs or GraphQL schemas
  • No need to sync types between frontend and backend
  • Catch breaking changes at compile-time, not runtime
// Server-side router
export const userRouter = createTRPCRouter({
  getById: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      return await userOperations.getById(input.id);
    }),
});

// Client-side usage - fully typed!
const { data } = api.users.getById.useQuery({ id: "123" });
//    ^? User | undefined

See the tRPC guide for more details.

Authentication: Better-Auth

Better-Auth handles user authentication and session management.

Why Better-Auth?

  • Type-safe - Full TypeScript support
  • Flexible - Email/password, OAuth, magic links, passkeys
  • Secure - HTTP-only cookies, CSRF protection, rate limiting
  • Modern - Built for React Server Components
  • Customizable - Extensible with plugins and adapters

Supported Auth Methods:

  • Email/Password
  • OAuth (Google, GitHub, and more)
  • Magic Links (passwordless)
  • Passkeys (WebAuthn)
// Server Component
import { getAuth } from "@/lib/auth/server";

const auth = await getAuth();

if (!auth.user) {
  redirect("/login");
}

// Client Component
"use client";
import { useAuth } from "@/lib/auth/client";

const { user, signIn, signOut } = useAuth();

See the Authentication guide for more details.

Styling & UI

Tailwind CSS

Tailwind CSS is our utility-first CSS framework.

Why Tailwind CSS?

  • Rapid development - Build UIs quickly with utility classes
  • Consistency - Design system baked into the framework
  • Responsive design - Mobile-first breakpoints built-in
  • Customization - Easily theme and extend
  • Tree-shaking - Only CSS you use ends up in production
  • No naming fatigue - No need to invent class names
// Example: Tailwind classes
<button className="rounded-lg bg-primary px-4 py-2 text-white hover:bg-primary/90">
  Click Me
</button>

Shadcn UI

Shadcn UI provides accessible, customizable UI components.

Why Shadcn UI?

  • Copy-paste components - Own your code, not imported from npm
  • Radix UI primitives - Accessibility built-in
  • Customizable - Modify components to fit your design
  • Tailwind-based - Consistent with our styling approach
  • No runtime overhead - Just React components

Available Components:

  • Forms: Input, Select, Checkbox, Radio, Textarea
  • Overlays: Dialog, Popover, Tooltip, Sheet
  • Navigation: Tabs, Dropdown Menu, Command
  • Feedback: Toast, Alert, Progress
  • And many more...
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

<Button variant="default" size="lg">
  Submit
</Button>

See the UI Components guide for more details.

Forms & Validation

React Hook Form

React Hook Form manages form state and validation.

Why React Hook Form?

  • Performance - Minimal re-renders
  • Easy integration - Works with UI libraries
  • Validation - Built-in or custom validation
  • TypeScript support - Fully typed
  • Small bundle size - ~9kb gzipped

Zod

Zod is our schema validation library.

Why Zod?

  • Type inference - Generate TypeScript types from schemas
  • Composable - Build complex validations from simple ones
  • Error messages - Customizable and translatable
  • Universal - Same schemas for client and server
  • No dependencies - Pure TypeScript
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
});

type FormData = z.infer<typeof schema>;

const form = useForm<FormData>({
  resolver: zodResolver(schema),
});

State Management

React Query (via tRPC)

React Query manages server state through tRPC.

Why React Query?

  • Automatic caching - Reduce network requests
  • Background updates - Keep data fresh
  • Optimistic updates - Instant UI feedback
  • Request deduplication - Avoid duplicate requests
  • Offline support - Handle network failures gracefully

Built into tRPC:

  • All tRPC hooks use React Query under the hood
  • Automatic type inference for cache keys
  • Integrated with tRPC's error handling
const { data, isLoading, error } = api.users.list.useQuery();

const mutation = api.users.create.useMutation({
  onSuccess: () => {
    // Invalidate cache to refetch
    utils.users.list.invalidate();
  },
});

Internationalization

next-intl

next-intl handles internationalization.

Why next-intl?

  • Type-safe translations - Catch missing keys at compile-time
  • Server Components support - Works with App Router
  • ICU message format - Rich formatting (plurals, dates, numbers)
  • Small bundle size - Only load needed translations
  • Routing integration - Locale prefixes handled automatically
import { useTranslations } from "next-intl";

function WelcomeMessage() {
  const t = useTranslations("common");
  
  return <h1>{t("welcome", { name: "John" })}</h1>;
  // Output: "Welcome, John!"
}

See the Internationalization guide for more details.

Email

React Email

React Email lets us build emails with React components.

Why React Email?

  • Component-based - Reuse React knowledge
  • Type-safe - TypeScript support
  • Preview - See emails in development
  • Responsive - Mobile-friendly by default
  • Client compatibility - Works across email clients
import { Button, Html, Text } from "@react-email/components";

export function WelcomeEmail({ name }: { name: string }) {
  return (
    <Html>
      <Text>Welcome, {name}!</Text>
      <Button href="https://example.com">Get Started</Button>
    </Html>
  );
}

File Storage

Firebase Storage / AWS S3

Firebase Storage or AWS S3 for file uploads.

Why Firebase Storage?

  • Easy setup - Quick to get started
  • SDKs - Official libraries for web and mobile
  • Generous free tier - Good for small projects
  • CDN included - Fast file delivery

Why AWS S3?

  • Scalability - Handle any amount of data
  • Reliability - 99.999999999% durability
  • Ecosystem - Integrates with other AWS services
  • Cost-effective - Pay only for what you use

Configuration is environment-based. See src/config/storage.ts for setup.

Development Tools

TypeScript ESLint

typescript-eslint for code linting.

  • Catch potential bugs
  • Enforce code style
  • Best practices

Prettier

Prettier for code formatting.

  • Consistent formatting
  • No debates about style
  • Automatic on save

Drizzle Kit

Drizzle Kit for database migrations.

  • Generate migrations from schema changes
  • Apply migrations to database
  • Introspect existing databases

Monitoring & Error Tracking

Sentry

Sentry for error tracking and performance monitoring.

Features:

  • Error tracking
  • Performance monitoring
  • Release tracking
  • User feedback

Configuration files:

  • sentry.server.config.ts - Server-side config
  • sentry.edge.config.ts - Edge runtime config

Deployment

Vercel

Optimized for Vercel deployment.

Why Vercel?

  • Zero config - Deploy with one click
  • Edge network - Fast global delivery
  • Preview deployments - Test before merging
  • Environment variables - Easy configuration
  • Analytics - Built-in performance tracking

Alternative platforms supported:

  • AWS (via SST or Amplify)
  • Google Cloud Run
  • Docker containers
  • Any Node.js host

Summary

This tech stack provides:

  • ✅ Type safety - From database to UI
  • ✅ Developer experience - Fast feedback loops
  • ✅ Performance - Server-first, optimized builds
  • ✅ Scalability - Proven technologies
  • ✅ Maintainability - Clear patterns and structure

Each technology was chosen to work seamlessly with the others, creating a cohesive and productive development experience.

On this page

Overview
Core Technologies
Framework: Next.js 16+
Language: TypeScript
Database: PostgreSQL + Drizzle ORM
API: tRPC
Authentication: Better-Auth
Styling & UI
Tailwind CSS
Shadcn UI
Forms & Validation
React Hook Form
Zod
State Management
React Query (via tRPC)
Internationalization
next-intl
Email
React Email
File Storage
Firebase Storage / AWS S3
Development Tools
TypeScript ESLint
Prettier
Drizzle Kit
Monitoring & Error Tracking
Sentry
Deployment
Vercel
Summary