Files
2025-11-29 18:29:07 +08:00

18 KiB

Grey Haven Project Setup Checklist

Comprehensive checklist for scaffolding new Grey Haven projects with TanStack Start, FastAPI, or both.

Pre-Project Planning

  • Define project scope (MVP features, future roadmap)

  • Choose architecture:

    • TanStack Start only (frontend + BFF)
    • FastAPI only (backend API)
    • Full-stack (TanStack Start + FastAPI)
    • Monorepo or separate repos
  • Define multi-tenant strategy:

    • Single-tenant (one customer)
    • Multi-tenant (multiple customers)
    • Tenant isolation: subdomain, custom domain, path-based
  • Plan authentication:

    • Better Auth (recommended for Grey Haven)
    • OAuth providers (Google, GitHub, etc.)
    • Email/password
    • Magic links
  • Database choice:

    • PostgreSQL (recommended for Grey Haven)
    • MySQL
    • SQLite (development only)
  • Hosting platform:

    • Vercel (TanStack Start)
    • Railway (FastAPI)
    • AWS (ECS, Lambda)
    • Self-hosted

Repository Setup

Initialize Git

  • Create repository (GitHub, GitLab, Bitbucket)

  • Initialize git: git init

  • Add .gitignore:

    node_modules/
    .env
    .env.local
    __pycache__/
    *.pyc
    .venv/
    dist/
    .output/
    .vercel/
    .DS_Store
    
  • Initial commit: git commit -m "Initial commit"

  • Create dev branch: git checkout -b dev

  • Set up branch protection (require PRs for main)

Project Structure

  • Create standard directories:

    .
    ├── .claude/                  # Claude Code config (optional)
    ├── apps/                     # Monorepo applications
    │   ├── web/                 # TanStack Start app
    │   └── api/                 # FastAPI app
    ├── packages/                 # Shared packages (monorepo)
    │   ├── shared-types/        # TypeScript types
    │   ├── ui/                  # Shared UI components
    │   └── utils/               # Shared utilities
    ├── docs/                     # Documentation
    ├── scripts/                  # Automation scripts
    └── .github/                  # GitHub workflows
        └── workflows/
    
  • Add README.md with:

    • Project description
    • Tech stack
    • Getting started guide
    • Environment variables
    • Deployment instructions

TanStack Start Setup

Installation

  • Create Vite project:

    npm create vite@latest my-app -- --template react-ts
    cd my-app
    
  • Install TanStack Start:

    npm install @tanstack/react-router @tanstack/react-query
    npm install -D @tanstack/router-vite-plugin @tanstack/router-devtools
    
  • Install dependencies:

    npm install zod drizzle-orm @better-auth/react
    npm install -D drizzle-kit tailwindcss postcss autoprefixer
    

Configure Vite

  • Update vite.config.ts:
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'
    import { TanStackRouterVite } from '@tanstack/router-vite-plugin'
    
    export default defineConfig({
      plugins: [
        react(),
        TanStackRouterVite()
      ],
      server: {
        port: 3000,
        proxy: {
          '/api': {
            target: 'http://localhost:8000',
            changeOrigin: true
          }
        }
      }
    })
    

Configure TailwindCSS

  • Initialize Tailwind:

    npx tailwindcss init -p
    
  • Update tailwind.config.js:

    export default {
      content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
      theme: {
        extend: {},
      },
      plugins: [],
    }
    
  • Add Tailwind directives to src/index.css:

    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    

Setup Drizzle (Database)

  • Install Drizzle:

    npm install drizzle-orm postgres
    npm install -D drizzle-kit
    
  • Create drizzle.config.ts:

    import { defineConfig } from 'drizzle-kit'
    
    export default defineConfig({
      schema: './src/db/schema.ts',
      out: './drizzle',
      dialect: 'postgresql',
      dbCredentials: {
        url: process.env.DATABASE_URL!
      }
    })
    
  • Create schema file src/db/schema.ts:

    import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core'
    
    export const users = pgTable('users', {
      id: uuid('id').defaultRandom().primaryKey(),
      email: text('email').notNull().unique(),
      name: text('name').notNull(),
      tenantId: uuid('tenant_id').notNull(),
      createdAt: timestamp('created_at').defaultNow(),
      updatedAt: timestamp('updated_at').defaultNow()
    })
    
    export const tenants = pgTable('tenants', {
      id: uuid('id').defaultRandom().primaryKey(),
      name: text('name').notNull(),
      slug: text('slug').notNull().unique(),
      createdAt: timestamp('created_at').defaultNow()
    })
    
  • Create migration: npx drizzle-kit generate

  • Run migration: npx drizzle-kit migrate

Setup Better Auth

  • Install Better Auth:

    npm install better-auth @better-auth/react
    
  • Create auth config src/lib/auth.ts:

    import { betterAuth } from 'better-auth'
    import { drizzleAdapter } from 'better-auth/adapters/drizzle'
    
    export const auth = betterAuth({
      database: drizzleAdapter(db, {
        provider: 'pg'
      }),
      emailAndPassword: {
        enabled: true
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!
        }
      }
    })
    
  • Create auth context for React

  • Add protected route wrapper

Router Setup

  • Create routes directory src/routes/
  • Create index route src/routes/index.tsx
  • Create auth routes (login, register, logout)
  • Create protected routes (dashboard, settings)
  • Configure router in src/main.tsx

Environment Variables

  • Create .env.local:

    DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
    BETTER_AUTH_SECRET=generate-with-openssl-rand-base64-32
    GOOGLE_CLIENT_ID=your-google-client-id
    GOOGLE_CLIENT_SECRET=your-google-client-secret
    VITE_API_URL=http://localhost:8000
    
  • Create .env.example (without secrets)

  • Add to .gitignore: .env.local

FastAPI Setup

Installation

  • Create project directory:

    mkdir my-api && cd my-api
    
  • Setup Python virtual environment:

    python -m venv .venv
    source .venv/bin/activate  # or .venv\Scripts\activate on Windows
    
  • Install FastAPI and dependencies:

    pip install fastapi uvicorn sqlmodel psycopg2-binary pydantic python-dotenv
    pip install pytest pytest-asyncio httpx  # Testing
    
  • Create requirements.txt:

    pip freeze > requirements.txt
    

Project Structure

  • Create standard structure:
    my-api/
    ├── app/
    │   ├── __init__.py
    │   ├── main.py              # FastAPI app
    │   ├── models/              # SQLModel models
    │   │   ├── __init__.py
    │   │   ├── user.py
    │   │   └── tenant.py
    │   ├── repositories/        # Data access layer
    │   │   ├── __init__.py
    │   │   ├── base.py
    │   │   └── user_repository.py
    │   ├── services/            # Business logic
    │   │   ├── __init__.py
    │   │   └── user_service.py
    │   ├── api/                 # API routes
    │   │   ├── __init__.py
    │   │   ├── deps.py          # Dependencies
    │   │   └── v1/
    │   │       ├── __init__.py
    │   │       ├── users.py
    │   │       └── auth.py
    │   ├── core/                # Config, security
    │   │   ├── __init__.py
    │   │   ├── config.py
    │   │   └── security.py
    │   └── db/                  # Database
    │       ├── __init__.py
    │       └── session.py
    ├── tests/
    │   ├── __init__.py
    │   ├── conftest.py
    │   └── test_users.py
    ├── alembic/                 # Migrations
    ├── .env
    └── requirements.txt
    

Database Setup (SQLModel)

  • Create SQLModel models app/models/user.py:

    from sqlmodel import SQLModel, Field
    from uuid import UUID, uuid4
    from datetime import datetime
    
    class User(SQLModel, table=True):
        __tablename__ = "users"
    
        id: UUID = Field(default_factory=uuid4, primary_key=True)
        email: str = Field(unique=True, index=True)
        name: str
        tenant_id: UUID = Field(foreign_key="tenants.id", index=True)
        created_at: datetime = Field(default_factory=datetime.utcnow)
        updated_at: datetime = Field(default_factory=datetime.utcnow)
    
  • Create database session app/db/session.py:

    from sqlmodel import create_engine, Session
    from app.core.config import settings
    
    engine = create_engine(settings.DATABASE_URL, echo=True)
    
    def get_session():
        with Session(engine) as session:
            yield session
    
  • Create tables: SQLModel.metadata.create_all(engine)

Repository Pattern

  • Create base repository app/repositories/base.py:

    from typing import Generic, TypeVar, Type, Optional, List
    from sqlmodel import Session, select
    from uuid import UUID
    
    ModelType = TypeVar("ModelType", bound=SQLModel)
    
    class BaseRepository(Generic[ModelType]):
        def __init__(self, model: Type[ModelType], session: Session):
            self.model = model
            self.session = session
    
        def get(self, id: UUID) -> Optional[ModelType]:
            return self.session.get(self.model, id)
    
        def get_all(self, tenant_id: UUID) -> List[ModelType]:
            statement = select(self.model).where(
                self.model.tenant_id == tenant_id
            )
            return self.session.exec(statement).all()
    
        def create(self, obj: ModelType) -> ModelType:
            self.session.add(obj)
            self.session.commit()
            self.session.refresh(obj)
            return obj
    
  • Create specific repositories (UserRepository, TenantRepository)

API Routes

  • Create main app app/main.py:

    from fastapi import FastAPI
    from fastapi.middleware.cors import CORSMiddleware
    from app.api.v1 import users, auth
    
    app = FastAPI(title="My API", version="1.0.0")
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["http://localhost:3000"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    app.include_router(auth.router, prefix="/api/v1/auth", tags=["auth"])
    app.include_router(users.router, prefix="/api/v1/users", tags=["users"])
    
    @app.get("/health")
    def health_check():
        return {"status": "healthy"}
    
  • Create route handlers app/api/v1/users.py

  • Add dependency injection for tenant_id, user auth

Environment Variables

  • Create .env:

    DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
    SECRET_KEY=generate-with-openssl-rand-hex-32
    ALGORITHM=HS256
    ACCESS_TOKEN_EXPIRE_MINUTES=30
    CORS_ORIGINS=http://localhost:3000
    
  • Create config app/core/config.py:

    from pydantic_settings import BaseSettings
    
    class Settings(BaseSettings):
        DATABASE_URL: str
        SECRET_KEY: str
        ALGORITHM: str = "HS256"
        ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
    
        class Config:
            env_file = ".env"
    
    settings = Settings()
    

Testing Setup

TypeScript/Vitest (Frontend)

  • Install Vitest:

    npm install -D vitest @vitest/ui @testing-library/react @testing-library/jest-dom
    
  • Create vitest.config.ts:

    import { defineConfig } from 'vitest/config'
    import react from '@vitejs/plugin-react'
    
    export default defineConfig({
      plugins: [react()],
      test: {
        globals: true,
        environment: 'jsdom',
        setupFiles: './src/test/setup.ts'
      }
    })
    
  • Add test script to package.json: "test": "vitest"

  • Create sample test src/test/example.test.ts

Pytest (Backend)

  • Create conftest.py with test fixtures:

    import pytest
    from fastapi.testclient import TestClient
    from sqlmodel import Session, create_engine, SQLModel
    from app.main import app
    from app.db.session import get_session
    
    @pytest.fixture(name="session")
    def session_fixture():
        engine = create_engine("sqlite:///:memory:")
        SQLModel.metadata.create_all(engine)
        with Session(engine) as session:
            yield session
    
    @pytest.fixture(name="client")
    def client_fixture(session: Session):
        def get_session_override():
            return session
        app.dependency_overrides[get_session] = get_session_override
        client = TestClient(app)
        yield client
        app.dependency_overrides.clear()
    
  • Add test script: pytest tests/ -v

  • Create sample test tests/test_users.py

Linting & Formatting

TypeScript (ESLint + Prettier)

  • Install ESLint:

    npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
    
  • Install Prettier:

    npm install -D prettier eslint-config-prettier
    
  • Create .eslintrc.json

  • Create .prettierrc

  • Add scripts to package.json:

    "lint": "eslint src --ext ts,tsx",
    "format": "prettier --write src"
    

Python (Ruff + Black)

  • Install Ruff and Black:

    pip install ruff black
    
  • Create pyproject.toml:

    [tool.black]
    line-length = 100
    
    [tool.ruff]
    line-length = 100
    select = ["E", "F", "I"]
    
  • Add to requirements.txt (dev dependencies)

CI/CD Setup

GitHub Actions

  • Create workflow .github/workflows/ci.yml:

    name: CI
    
    on:
      push:
        branches: [main, dev]
      pull_request:
        branches: [main, dev]
    
    jobs:
      test-frontend:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: actions/setup-node@v3
            with:
              node-version: '20'
          - run: npm ci
          - run: npm run lint
          - run: npm test
          - run: npm run build
    
      test-backend:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: actions/setup-python@v4
            with:
              python-version: '3.11'
          - run: pip install -r requirements.txt
          - run: pytest tests/ -v
    
  • Add deployment workflow (Vercel, Railway, AWS)

Database Migrations

  • Setup Drizzle migrations (TanStack Start):

    • npx drizzle-kit generate to create migrations
    • npx drizzle-kit migrate to apply migrations
  • Setup Alembic (FastAPI):

    pip install alembic
    alembic init alembic
    
    • Configure alembic.ini with DATABASE_URL
    • Create migration: alembic revision --autogenerate -m "message"
    • Apply migration: alembic upgrade head

Secrets Management

  • Choose secrets manager:

    • Doppler (recommended for Grey Haven)
    • AWS Secrets Manager
    • Environment variables (for local dev only)
  • Install Doppler CLI (if using Doppler):

    # Install: https://docs.doppler.com/docs/install-cli
    doppler login
    doppler setup
    
  • Never commit secrets to git

  • Use .env.example for documentation

Deployment

Vercel (TanStack Start)

  • Install Vercel CLI: npm i -g vercel
  • Connect to Vercel: vercel link
  • Configure environment variables in Vercel dashboard
  • Deploy: vercel --prod
  • Setup custom domain (if needed)

Railway (FastAPI)

  • Install Railway CLI: npm i -g @railway/cli
  • Login: railway login
  • Initialize: railway init
  • Add environment variables: railway variables
  • Deploy: railway up

Multi-Tenant Configuration

  • Add tenant_id to all relevant tables

  • Create RLS policies (if using PostgreSQL RLS)

  • Repository pattern enforces tenant filtering

  • Subdomain routing (if applicable):

    • tenant1.myapp.com → tenant_id = uuid1
    • tenant2.myapp.com → tenant_id = uuid2
  • Tenant signup flow:

    • Create tenant record
    • Create owner user
    • Associate user with tenant
    • Generate invitation links

Monitoring & Observability

  • Setup error tracking (Sentry, Datadog)
  • Add structured logging (Pino for Node, structlog for Python)
  • Setup metrics (Prometheus, Datadog)
  • Create health check endpoint (/health)
  • Setup uptime monitoring (Pingdom, UptimeRobot)

Documentation

  • README.md with setup instructions
  • API documentation (auto-generated with FastAPI /docs)
  • Architecture diagram (optional, but recommended)
  • Environment variables documented in .env.example
  • Contributing guide (if open-source or team project)

Scoring

  • 80+ items checked: Excellent - Production-ready setup
  • 60-79 items: Good - Most setup complete ⚠️
  • 40-59 items: Fair - Missing important pieces 🔴
  • <40 items: Poor - Not ready for development

Priority Items

Complete these first:

  1. Repository setup - Git, structure, README
  2. Database schema - Models, migrations
  3. Authentication - Better Auth, protected routes
  4. Testing setup - Vitest, pytest
  5. CI/CD - GitHub Actions, deployment

Common Pitfalls

Don't:

  • Commit .env files (use .env.example instead)
  • Skip testing setup (add it from day one)
  • Ignore linting (consistent code quality matters)
  • Deploy without health checks
  • Skip multi-tenant isolation (add tenant_id early)

Do:

  • Use repository pattern for data access
  • Set up CI/CD early (automate testing)
  • Document environment variables
  • Test authentication thoroughly
  • Plan for scale (database indexes, caching)

Total Items: 130+ setup checks Critical Items: Repository, Database, Auth, Testing, Deployment Coverage: TanStack Start, FastAPI, Multi-tenant, Testing, CI/CD Last Updated: 2025-11-10