2025-12-19 10:05:01 +00:00

192 lines
5.9 KiB
Markdown

# Ollis Turborepo Test
A full-stack TypeScript monorepo demonstrating modern development practices with automatic API client generation and type-safe end-to-end data flow.
## Architecture Overview
This monorepo showcases a **type-safe, auto-generated API contract** between backend and frontend:
```
Backend (Fastify) → OpenAPI Spec → Generated API Client → Frontend (SolidJS)
```
### Key Design Decisions
1. **Schema-First API Design**: Backend schemas (TypeBox/JSONSchema) are the single source of truth. The OpenAPI specification is automatically generated from these schemas and actual route definitions using Fastify's `@fastify/swagger`, ensuring the API contract always reflects the actual backend implementation.
2. **Automatic Client Generation**: The `api-client-backend` package uses `@hey-api/openapi-ts` to generate a fully typed API client from the OpenAPI spec, ensuring type safety across the stack.
3. **Turborepo Watch Mode**: Development workflow uses Turborepo's watch feature to orchestrate the entire pipeline automatically - when you change an API schema, the OpenAPI spec is regenerated, the client is regenerated, and the frontend picks up changes via HMR.
4. **Bare Metal Development**: Apps and packages run directly on the host machine (not containerized) for faster iteration, better debugging, and native tool integration. Only external dependencies like PostgreSQL run in Docker containers via `docker-compose`.
## What's inside?
This Turborepo includes the following packages/apps:
### Apps
- `frontend`: a [SolidJS](https://www.solidjs.com/) app with CRUD UI for persons and pets
- `backend`: a [Fastify](https://fastify.dev/) REST API with PostgreSQL and Kysely ORM
### Packages
- `@olli/api-client-backend`: Auto-generated TypeScript API client based on backend's OpenAPI spec
- `@olli/ts-config`: Shared TypeScript configurations
### Utilities
- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [Biome](https://biomejs.dev/) for code formatting and linting
- [Kysely](https://kysely.dev/) for type-safe SQL queries
- [Vitest](https://vitest.dev/) for testing
## Development Workflow
### Initial Setup
1. **Copy environment files:**
```sh
cp .env.example .env
cp apps/frontend/.env.example apps/frontend/.env
cp apps/backend/.env.example apps/backend/.env
```
2. **Start local dependencies (PostgreSQL):**
```sh
docker compose up -d
```
- PGAdmin: http://localhost:5050
3. **Install dependencies:**
```sh
pnpm install
```
### Running Development Servers
The recommended way to develop is using Turborepo's watch mode:
```sh
pnpm run dev:watch
```
Or with global Turbo:
```sh
turbo watch dev:watch export-openapi generate
```
> `turbo watch` only works for non-persistent tasks. So `dev` tasks are not affected, therefore we need to run the `dev:watch` task here, so the apps actually watch changes by themselves.
#### What `dev:watch` Does
This single command orchestrates the entire development workflow:
1. **Starts Development Servers** (`dev:watch` task):
- Backend: Runs on http://localhost:3000 with nodemon for auto-restart
- Frontend: Runs on http://localhost:5173 with Vite HMR
2. **Watches for API Changes** (`export-openapi` task):
- Monitors backend source files (`src/**/*.ts`)
- When API schemas change, automatically exports updated OpenAPI spec to `packages/api-client-backend/openapi.json`
3. **Regenerates API Client** (`generate` task):
- Watches the OpenAPI spec file
- When spec changes, regenerates TypeScript client in `packages/api-client-backend/generated/`
- Thanks to PNPM workspace linking, frontend immediately sees the updated types
4. **Updates Frontend** (automatic via Vite HMR):
- Vite detects changes in workspace dependencies
- Frontend auto-refreshes with new API types and methods
**The Flow:**
```
Backend code change
Nodemon restarts backend server
Turbo watch detects file change
export-openapi script runs → generates openapi.json
Turbo runs generate task → regenerates API client
Vite HMR detects api-client-backend change
Frontend reloads with updated types ✨
```
This workflow ensures **zero manual steps** - just change your API schema and watch it propagate through the entire stack!
### Alternative Development Commands
Develop all apps and packages:
```sh
turbo dev
```
Develop a specific package:
```sh
turbo dev --filter=backend
turbo dev --filter=frontend
```
### Build
Build all apps and packages:
```sh
turbo build
```
Build a specific package:
```sh
turbo build --filter=backend
```
### Testing
Run all tests:
```sh
turbo test
```
Run tests in watch mode:
```sh
turbo test:watch
```
## Project Structure
```
apps/
backend/ # Fastify API server
src/
features/ # Feature-based organization
demo/
*.routes.ts # API route handlers
*.schema.ts # Zod/TypeBox schemas
*.repository.ts
db/ # Database setup and migrations
scripts/
export-openapi.ts # OpenAPI spec generator
frontend/ # SolidJS web app
src/
person-crud.tsx # Person management UI
pet-crud.tsx # Pet management UI
api.ts # Configured API client
packages/
api-client-backend/ # Generated API client
generated/ # Auto-generated (don't edit!)
openapi.json # Source of truth from backend
```
## Useful Links
Learn more about the power of Turborepo:
- [Tasks](https://turborepo.com/docs/crafting-your-repository/running-tasks)
- [Caching](https://turborepo.com/docs/crafting-your-repository/caching)
- [Remote Caching](https://turborepo.com/docs/core-concepts/remote-caching)
- [Filtering](https://turborepo.com/docs/crafting-your-repository/running-tasks#using-filters)
- [Configuration Options](https://turborepo.com/docs/reference/configuration)
- [CLI Usage](https://turborepo.com/docs/reference/command-line-reference)