Monorepo Foundations
Monorepo Foundations
Our project utilizes a monorepo architecture managed by pnpm and Turborepo to streamline development, ensure consistency, and optimize build processes across multiple applications and shared packages.
Core Concepts
- pnpm Workspaces: Manages dependencies across the entire project, allowing shared libraries and efficient installation.
- Turborepo: Provides intelligent build caching and task orchestration, significantly speeding up builds and tests.
Package Management
- pnpm is the designated package manager (v10.6.5 or higher).
- Use
workspace:*
syntax inpackage.json
files to link internal packages. - Install all dependencies from the root directory:
pnpm install
Workspace Structure
The monorepo is organized into two primary directories at the root:
apps/
: Contains deployable applications.web/
: The main web application (Vite + React Router).native/
: The React Native mobile application (Expo).
packages/
: Contains shared libraries, utilities, and configurations used across applications.api/
: Backend API server (Hono) and client types.api-hooks/
: TanStack Query hooks generated from the API spec.auth/
: Authentication logic and utilities.config/
: Shared configurations (ESLint, TypeScript, etc.).database/
: Drizzle ORM schema, client, and migration utilities.email/
: Email templates and sending logic.i18n/
: Internationalization setup.payments/
: Payment provider integrations (e.g., Stripe).push-notifications/
: Push notification setup.storage/
: Cloud storage utilities.typescript-config/
: Base TypeScript configurations.
Common Commands
Execute these commands from the root of the monorepo:
# Install all dependencies
pnpm install
# Start all applications in development mode
pnpm dev
# Build all applications and packages
pnpm build
# Run linters across the codebase
pnpm lint
# Run tests for all packages
pnpm test
# Clean all build artifacts and node_modules
pnpm clean
# Start a specific application (e.g., web)
pnpm --filter web dev
# Run a command within a specific package (e.g., generate DB migrations)
pnpm --filter @repo/database db:generate
Adding Dependencies
Always use pnpm
with the --filter
flag to add dependencies to specific packages:
# Add a production dependency to 'web' app
pnpm --filter web add react-map-gl
# Add a development dependency to 'api' package
pnpm --filter @repo/api add -D @types/node
Add shared development tools (like linters, test runners) to the root package.json
:
# Add husky as a root development dependency
pnpm add -Dw husky
Creating New Packages
- Create the new directory under
packages/
(e.g.,packages/new-utility
). - Initialize a
package.json
(e.g.,pnpm init
). Ensure the package name is scoped (e.g.,@repo/new-utility
). - Set up
tsconfig.json
, extending from@repo/typescript-config/base.json
. - Add necessary build/dev scripts to its
package.json
. - Update
pnpm-workspace.yaml
if necessary (usually automatic forpackages/*
). - Configure its pipeline tasks in the root
turbo.json
.
Turborepo (turbo.json
)
Turborepo orchestrates tasks defined in the pipeline
section of turbo.json
. Key aspects include:
- Task Dependencies (
dependsOn
): Defines the execution order (e.g.,build
depends on^build
, meaning build dependencies first). - Caching (
cache
,outputs
): Specifies which files/outputs to cache, speeding up subsequent runs. - Inputs: Defines files/env variables that influence a task's output. Changes to inputs invalidate the cache.
Refer to the root turbo.json
for the specific pipeline configuration.