# Turborepo Monorepo Best Practices — MeshiTrack > Instruction file for managing the monorepo workspace. ## Workspace Structure ``` MeshiTrack/ ├── packages/ │ ├── api/ # NestJS backend → @meshitrack/api │ ├── web/ # Next.js frontend → @meshitrack/web │ └── shared/ # Shared types & DTOs → @meshitrack/shared ├── docker/ # Docker configs (not a package) ├── docs/ # Documentation (not a package) ├── turbo.json # Turborepo pipeline config ├── package.json # Root workspace config ├── tsconfig.base.json ├── .eslintrc.js ├── .prettierrc └── .gitignore ``` ## Root package.json ```json { "name": "meshitrack", "private": true, "workspaces": ["packages/*"], "scripts": { "dev": "turbo run dev", "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test", "typecheck": "turbo run typecheck", "clean": "turbo run clean" }, "devDependencies": { "turbo": "^2.x", "typescript": "^5.x", "eslint": "^9.x", "prettier": "^3.x" } } ``` ## turbo.json Configuration ```json { "$schema": "https://turbo.build/schema.json", "globalDependencies": ["**/.env.*local"], "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }, "dev": { "dependsOn": ["^build"], "cache": false, "persistent": true }, "lint": { "dependsOn": ["^build"] }, "test": { "dependsOn": ["^build"] }, "typecheck": { "dependsOn": ["^build"] }, "clean": { "cache": false } } } ``` ### Key concepts - **`dependsOn: ["^build"]`**: Before running a task in a package, first build all its dependencies. This ensures `shared` is built before `api` or `web` run. - **`outputs`**: What Turborepo caches. If outputs haven't changed, Turborepo replays from cache. - **`persistent: true`**: For long-running dev servers that shouldn't be cached. - **`cache: false`**: Disables caching for tasks that should always run. ## Package Dependencies ### Shared package is the foundation ``` @meshitrack/shared ← @meshitrack/api ← @meshitrack/web ``` Both `api` and `web` depend on `shared`, but never on each other. ### Reference shared in package.json ```json // packages/api/package.json { "name": "@meshitrack/api", "dependencies": { "@meshitrack/shared": "workspace:*" } } // packages/web/package.json { "name": "@meshitrack/web", "dependencies": { "@meshitrack/shared": "workspace:*" } } ``` ### Import from shared ```typescript // In packages/api or packages/web import { Product, NutritionInfo, ProductCategory } from '@meshitrack/shared'; import { CreateProductSchema } from '@meshitrack/shared/validation'; ``` ## Shared Package Setup ### packages/shared/package.json ```json { "name": "@meshitrack/shared", "version": "0.0.1", "private": true, "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "./validation": { "types": "./dist/validation/index.d.ts", "default": "./dist/validation/index.js" } }, "scripts": { "build": "tsc", "dev": "tsc --watch", "clean": "rm -rf dist", "typecheck": "tsc --noEmit" }, "dependencies": { "zod": "^3.x" }, "devDependencies": { "typescript": "^5.x" } } ``` ### packages/shared/tsconfig.json ```json { "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src", "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*"] } ``` ### Shared package must be platform-agnostic Rules for code in `packages/shared`: - **No** Node.js imports (`fs`, `path`, `http`, etc.) - **No** NestJS decorators or imports - **No** Next.js imports - **No** DOM/browser APIs - Only pure TypeScript: types, interfaces, enums, Zod schemas, utility functions ## TypeScript Configuration ### Root tsconfig.base.json ```json { "compilerOptions": { "target": "ES2022", "lib": ["ES2022"], "module": "commonjs", "moduleResolution": "node", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true, "sourceMap": true, "incremental": true }, "exclude": ["node_modules", "dist"] } ``` Each package extends this and overrides as needed (e.g., `web` uses `"jsx": "preserve"`, `"module": "esnext"`). ## Development Workflow ### Start all packages in dev mode ```bash npm run dev # Turborepo runs: shared (build) → then api (dev) + web (dev) in parallel ``` ### Build all packages ```bash npm run build # Turborepo builds shared first, then api and web in parallel ``` ### Run tasks for a specific package ```bash npx turbo run dev --filter=@meshitrack/api npx turbo run test --filter=@meshitrack/web ``` ### Add a dependency to a specific package ```bash cd packages/api npm install @nestjs/schedule # Or from root: npm install @nestjs/schedule --workspace=packages/api ``` ## Caching ### Turborepo remote caching (optional) For CI, consider enabling remote caching to share build cache across machines: ```bash npx turbo login npx turbo link ``` Or use a self-hosted cache server for full self-hosted setup. ### Local caching Turborepo caches locally in `node_modules/.cache/turbo` by default. It's fast and requires no setup. ## CI Integration ```yaml # .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: 'npm' - run: npm ci - run: npx turbo run build lint test typecheck ``` ## Common Pitfalls 1. **Forgetting to build shared**: If `api` or `web` can't find types from `shared`, run `npm run build` from root. The `dependsOn: ["^build"]` config handles this in Turborepo tasks. 2. **Circular dependencies**: Never import from `api` in `web` or vice versa. Only import from `shared`. 3. **Version mismatches**: Keep TypeScript versions aligned across all packages. Pin in root `devDependencies`. 4. **Large `node_modules`**: Use `npm` workspaces hoisting. Most dependencies are installed at root level. Only package-specific versions go in package-level `node_modules`.