| name | monorepo-structure |
| description | Turborepo monorepo setup with Yarn workspaces, shared configs, and development workflows. Use this skill when working with monorepo setup, shared packages, workspace dependencies, or build orchestration. |
| license | MIT |
Monorepo Structure Skill
Overview
This skill covers the monorepo architecture, workspace management, and development workflows for the Game Critique project using Turborepo and Yarn workspaces.
Monorepo Architecture
Project Structure
Game-Critique/
├── .github/ # GitHub configs and workflows
│ └── skills/ # Copilot skills
├── apps/ # Application workspaces
│ ├── api/ # NestJS GraphQL API
│ ├── native/ # Expo React Native app
│ └── web/ # React admin panel
├── packages/ # Shared packages
│ └── typescript-config/ # Shared TypeScript configs
├── package.json # Root package.json
├── turbo.json # Turborepo configuration
├── yarn.lock # Yarn lockfile
└── compose.yml # Docker Compose setup
Package Manager: Yarn 4.5.3
Workspace Configuration
{
"private": true,
"workspaces": [
"./apps/*",
"./packages/*"
],
"packageManager": "yarn@4.5.3",
"engines": {
"node": ">=18"
}
}
Yarn Commands
yarn install
yarn workspace api add @nestjs/graphql
yarn workspace web add @tanstack/react-query
yarn workspace native add tamagui
yarn workspace api add -D jest
yarn workspace api remove @nestjs/graphql
yarn workspace api dev
yarn workspace web build
yarn workspaces foreach run build
yarn workspaces foreach run test
yarn upgrade-interactive
Turborepo Configuration
Basic Setup
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"tasks": {
"build": {
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
},
"test": {
"cache": false,
"persistent": false
},
"lint": {
"outputs": []
},
"clean": {
"cache": false
}
}
}
Task Orchestration
Build Dependencies:
^build means "build dependencies first"
- Ensures packages are built before apps that depend on them
Caching:
cache: false for dev servers and tests
persistent: true for long-running processes
- Outputs specified for build artifacts
Turborepo Commands
turbo run build
turbo run dev
turbo run test
turbo run lint
turbo run build --filter=api
turbo run dev --filter=web
turbo run test --filter=native
turbo run dev --filter=web --filter=api
turbo run build --force
turbo run clean
Root Scripts
{
"scripts": {
"dev": "turbo run dev",
"dev:web": "turbo run dev --filter=web --filter=api",
"dev:api": "turbo run dev --filter=api",
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\" --ignore-path .gitignore",
"clean": "turbo run clean && rm -rf node_modules"
}
}
Shared Packages
TypeScript Configuration Package
packages/
typescript-config/
├── package.json
├── base.json # Base config
├── nestjs.json # NestJS-specific
├── nextjs.json # Next.js-specific
└── react-native-library.json
Package.json:
{
"name": "@repo/typescript-config",
"version": "0.0.0",
"private": true,
"files": [
"base.json",
"nestjs.json",
"nextjs.json",
"react-native-library.json"
]
}
Base Config:
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}
NestJS Config:
{
"extends": "./base.json",
"compilerOptions": {
"module": "commonjs",
"target": "ES2021",
"lib": ["ES2021"],
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"incremental": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"strictPropertyInitialization": false
}
}
Using Shared Config
{
"extends": "@repo/typescript-config/nestjs.json",
"compilerOptions": {
"outDir": "./dist",
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Workspace Dependencies
Internal Workspace References
{
"name": "api",
"dependencies": {
"@repo/typescript-config": "*"
}
}
Version Management
- Use
* for internal workspace dependencies
- Yarn resolves to local workspace automatically
- Changes to packages immediately available to apps
Development Workflows
Starting Development Environment
Full Stack:
yarn dev
yarn dev:web
yarn dev:api
Individual Apps:
cd apps/api
yarn start:db
yarn dev
cd apps/web
yarn dev
cd apps/native
yarn dev-local
yarn dev
Building for Production
yarn build
cd apps/api && yarn build
cd apps/web && yarn build
turbo run build --filter=api
turbo run build --filter=web
Code Quality
yarn lint
yarn format
cd apps/api && yarn lint
cd apps/web && yarn format
Environment Variables
Organization
apps/
api/
.env
.env.local
.env.example
web/
.env
.env.local
native/
.env.local
.env.production
Loading Environment Variables
API (NestJS):
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
})
export class AppModule {}
Web (Vite):
const apiUrl = import.meta.env.VITE_API_URL;
Native (Expo):
const apiUrl = process.env.EXPO_PUBLIC_GRAPHQL_ENDPOINT;
Turbo Global Dependencies
{
"globalDependencies": ["**/.env.*local"]
}
This ensures Turbo cache is invalidated when env files change.
Docker Compose Integration
Root Compose File
name: game-critique
include:
- path: apps/api/compose.yml
API Services
services:
postgres:
image: postgres:15
ports:
- "5432:5432"
environment:
POSTGRES_USER: game_critique
POSTGRES_PASSWORD: password
POSTGRES_DB: game_critique
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Docker Commands
docker-compose up -d
docker-compose down
docker-compose logs -f postgres
docker-compose logs -f redis
docker-compose down -v
Git Hooks with Husky
Setup
{
"scripts": {
"prepare": "husky install"
},
"lint-staged": {
"*.{ts,tsx}": [
"prettier --write"
]
}
}
Pre-commit Hook
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
GraphQL Schema Sharing
Configuration
schema: "apps/api/src/schema.gql"
Workflow
- API generates schema on startup
- Schema file at
apps/api/src/schema.gql
- Web and Native reference this schema for codegen
- IDE uses schema for autocomplete
const config: CodegenConfig = {
schema: "http://localhost:3001/graphql",
documents: "src/**/*.graphql",
};
const config: CodegenConfig = {
schema: process.env.EXPO_PUBLIC_GRAPHQL_ENDPOINT,
documents: ["modules/**/*.graphql"],
};
Adding a New Workspace
1. Create Workspace Directory
mkdir -p apps/new-app
cd apps/new-app
2. Initialize Package
yarn init -p
3. Configure Package.json
{
"name": "new-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "...",
"build": "...",
"test": "..."
},
"dependencies": {
"@repo/typescript-config": "*"
}
}
4. Install Dependencies
yarn install
5. Add Turbo Task (if needed)
{
"tasks": {
"new-task": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
Creating Shared Packages
1. Create Package Structure
mkdir -p packages/shared-utils
cd packages/shared-utils
2. Setup Package
{
"name": "@repo/shared-utils",
"version": "0.0.0",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"devDependencies": {
"@repo/typescript-config": "*",
"typescript": "^5.0.0"
}
}
3. TypeScript Config
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
4. Export Functions
export function formatDate(date: Date): string {
return date.toISOString();
}
export function slugify(text: string): string {
return text.toLowerCase().replace(/\s+/g, '-');
}
5. Build Package
cd packages/shared-utils
yarn build
6. Use in Apps
{
"dependencies": {
"@repo/shared-utils": "*"
}
}
import { formatDate, slugify } from '@repo/shared-utils';
Troubleshooting
Dependency Issues
yarn clean
yarn install
turbo run build
Turbo Cache Issues
turbo run build --force
rm -rf node_modules/.cache/turbo
Workspace Not Found
yarn workspaces list
Port Conflicts
lsof -i :3000
lsof -i :5173
kill -9 <PID>
TypeScript Errors
turbo run build --filter=@repo/typescript-config
turbo run build
Best Practices
1. Use Workspace Protocol
"dependencies": {
"@repo/typescript-config": "*"
"@repo/typescript-config": "1.0.0"
}
2. Keep Root Clean
- Only workspace orchestration in root
- App-specific code in workspaces
- Shared utilities in packages
3. Consistent Naming
- Apps: descriptive names (api, web, native)
- Packages: scoped with @repo/* prefix
- Scripts: consistent across workspaces
4. Proper Caching
- Enable cache for builds
- Disable cache for dev servers
- Specify outputs for cached tasks
5. Environment Isolation
- Each app manages its own env vars
- Use different prefixes (VITE_, EXPO_PUBLIC_)
- Never commit .env.local files
6. Build Order
- Use
dependsOn for build dependencies
- Shared packages build before apps
- Parallelize independent builds
7. Version Pinning
- Pin external dependencies to exact versions
- Use workspace protocol for internal deps
- Document major version upgrades
Development Commands Reference
yarn install
yarn dev
yarn dev:web
yarn dev:api
yarn build
yarn lint
yarn format
yarn clean
yarn workspace api dev
yarn workspace web build
yarn workspace native test
turbo run build
turbo run dev --filter=api
turbo run test --filter=web --filter=api
turbo run build --force
docker-compose up -d
docker-compose down
docker-compose logs -f
yarn workspaces list
yarn workspaces foreach run build
yarn workspace api add lodash
CI/CD Considerations
Caching Strategy
- name: Setup Turbo cache
uses: actions/cache@v3
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
Affected Apps Only
turbo run build --filter=[HEAD^1]
turbo run test --filter=[main...HEAD]
Parallel Jobs
strategy:
matrix:
app: [api, web, native]
steps:
- run: turbo run build --filter=${{ matrix.app }}