🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples de Turbo Monorepo
Exemples du système de build Turbo monorepo incluant la configuration workspace, l'orchestration des tâches, et les stratégies de caching
💻 Configuration de Base Turbo Monorepo json
🟢 simple
⭐⭐
Configuration complète de Turbo monorepo avec multiples applications et paquets
⏱️ 20 min
🏷️ turbo, monorepo, setup
Prerequisites:
Node.js, pnpm/yarn/npm, TypeScript
// Turbo Monorepo Basic Setup
// 1. Installation
// npm install turbo --save-dev
// or: pnpm add -D turbo
// or: yarn add -D turbo
// 2. package.json (Root)
{
"name": "my-turbo-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test",
"clean": "turbo run clean && rm -rf node_modules",
"format": "prettier --write "**/*.{ts,tsx,md}"",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "turbo run build --filter=!@repo/docs && changeset publish"
},
"devDependencies": {
"turbo": "^2.0.0",
"prettier": "^3.0.0",
"changesets": "^2.26.0"
},
"packageManager": "[email protected]"
}
// 3. turbo.json - Root configuration
{
"$schema": "https://turbo.build/schema.json",
"globalEnv": [
"NODE_ENV",
"NEXT_PUBLIC_API_URL",
"DATABASE_URL"
],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**", "!.next/**/(!*.next)"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"clean": {
"cache": false
},
"type-check": {
"dependsOn": ["^build"]
},
"format": {
"outputs": []
}
},
"globalDependencies": [
"**/.env.*local",
"turbo.json",
"package.json",
"pnpm-lock.yaml",
"yarn.lock",
"npm-shrinkwrap.json"
]
}
// 4. Project structure
/*
my-turbo-monorepo/
├── apps/
│ ├── web/ # Next.js frontend application
│ │ ├── package.json
│ │ ├── next.config.js
│ │ └── turbo.json
│ ├── api/ # Backend API (Express/Fastify)
│ │ ├── package.json
│ │ └── turbo.json
│ └── docs/ # Documentation site
│ ├── package.json
│ └── turbo.json
├── packages/
│ ├── ui/ # Shared UI components
│ │ ├── package.json
│ │ ├── turbo.json
│ │ └── src/
│ ├── utils/ # Shared utilities
│ │ ├── package.json
│ │ ├── turbo.json
│ │ └── src/
│ ├── config/ # Shared configuration
│ │ ├── package.json
│ │ ├── eslint.config.js
│ │ ├── tsconfig.base.json
│ │ └── turbo.json
│ └── types/ # Shared TypeScript types
│ ├── package.json
│ ├── turbo.json
│ └── src/
├── package.json
├── turbo.json
├── pnpm-workspace.yaml
└── .gitignore
*/
// 5. pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
// 6. Example app configuration (apps/web/turbo.json)
{
"extends": ["//"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}
// 7. Example package configuration (packages/ui/turbo.json)
{
"extends": ["//"],
"pipeline": {
"build": {
"outputs": ["dist/**"]
},
"lint": {},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}
// 8. TypeScript configuration (packages/config/tsconfig.base.json)
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "ES2022"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@repo/ui": ["../../packages/ui/src"],
"@repo/utils": ["../../packages/utils/src"],
"@repo/types": ["../../packages/types/src"],
"@repo/config/*": ["./"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}
// 9. Environment variables (.env.example)
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
# API
NEXT_PUBLIC_API_URL="http://localhost:3001/api"
# Authentication
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-secret-key"
# External services
REDIS_URL="redis://localhost:6379"
STRIPE_API_KEY="sk_test_..."
SENTRY_DSN="https://your-sentry-dsn"
💻 Workspace Multi-Application Turbo typescript
🟡 intermediate
⭐⭐⭐⭐
Configuration avancée de monorepo avec frontend Next.js, backend Express, et paquets partagés
⏱️ 35 min
🏷️ turbo, monorepo, nextjs, express
Prerequisites:
Turbo basics, Next.js, Express, TypeScript
// Turbo Multi-Application Workspace Example
// 1. Next.js Application (apps/web/package.json)
{
"name": "@repo/web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest",
"type-check": "tsc --noEmit"
},
"dependencies": {
"next": "^14.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*",
"@repo/types": "workspace:*",
"axios": "^1.6.0",
"next-auth": "^4.24.0"
},
"devDependencies": {
"@repo/config/eslint": "workspace:*",
"@repo/config/typescript": "workspace:*",
"@types/node": "^20.0.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"tailwindcss": "^3.3.0",
"typescript": "^5.3.0"
}
}
// 2. Next.js Configuration (apps/web/next.config.js)
const withTM = require('next-transpile-modules')([
'@repo/ui',
'@repo/utils',
'@repo/types'
])
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@repo/ui', '@repo/utils', '@repo/types'],
experimental: {
optimizePackageImports: ['@repo/ui']
},
images: {
domains: ['example.com', 'images.unsplash.com']
},
env: {
CUSTOM_KEY: process.env.CUSTOM_KEY
}
}
module.exports = nextConfig
// 3. Express Backend Application (apps/api/package.json)
{
"name": "@repo/api",
"version": "0.1.0",
"private": true,
"main": "dist/index.js",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"lint": "eslint src --max-warnings 0",
"test": "jest",
"type-check": "tsc --noEmit"
},
"dependencies": {
"express": "^4.18.0",
"cors": "^2.8.0",
"helmet": "^7.1.0",
"compression": "^1.7.0",
"morgan": "^1.10.0",
"@repo/utils": "workspace:*",
"@repo/types": "workspace:*",
"zod": "^3.22.0",
"prisma": "^5.6.0"
},
"devDependencies": {
"@repo/config/eslint": "workspace:*",
"@repo/config/typescript": "workspace:*",
"@types/express": "^4.17.0",
"@types/cors": "^2.8.0",
"@types/compression": "^1.7.0",
"@types/morgan": "^1.9.0",
"tsx": "^4.6.0",
"typescript": "^5.3.0"
}
}
// 4. Express Application (apps/api/src/index.ts)
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import compression from 'compression'
import morgan from 'morgan'
import { logger } from '@repo/utils/logger'
import { ApiResponse } from '@repo/types/api'
const app = express()
const PORT = process.env.PORT || 3001
// Middleware
app.use(helmet())
app.use(compression())
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true
}))
app.use(morgan('combined'))
app.use(express.json({ limit: '10mb' }))
app.use(express.urlencoded({ extended: true }))
// Health check endpoint
app.get('/health', (req, res): ApiResponse => {
return res.json({
success: true,
message: 'API is healthy',
timestamp: new Date().toISOString()
})
})
// API routes
app.get('/api/users', async (req, res) => {
try {
// Simulate database call
const users = [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' }
]
return res.json({
success: true,
data: users,
count: users.length
})
} catch (error) {
logger.error('Error fetching users:', error)
return res.status(500).json({
success: false,
message: 'Internal server error'
})
}
})
// Error handling middleware
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.error('Unhandled error:', err)
return res.status(500).json({
success: false,
message: 'Internal server error'
})
})
app.listen(PORT, () => {
logger.info(`API server running on port ${PORT}`)
})
export default app
// 5. Shared UI Components Package (packages/ui/package.json)
{
"name": "@repo/ui",
"version": "0.1.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./styles.css": "./dist/styles.css"
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint src --max-warnings 0",
"test": "jest",
"type-check": "tsc --noEmit",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"clsx": "^2.0.0",
"tailwind-merge": "^2.0.0",
"@repo/types": "workspace:*"
},
"devDependencies": {
"@repo/config/eslint": "workspace:*",
"@repo/config/typescript": "workspace:*",
"@storybook/addon-essentials": "^7.5.0",
"@storybook/react": "^7.5.0",
"@storybook/react-vite": "^7.5.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"tailwindcss": "^3.3.0",
"tsup": "^7.2.0",
"typescript": "^5.3.0"
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
// 6. UI Components Build Configuration (packages/ui/tsup.config.ts)
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
external: ['react', 'react-dom'],
onSuccess: 'npm run build:css'
})
// 7. Shared Components (packages/ui/src/Button.tsx)
import React from 'react'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
loading?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', loading, children, disabled, ...props }, ref) => {
const baseStyles = 'inline-flex items-center justify-center font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2'
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',
outline: 'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus:ring-blue-500'
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-sm',
lg: 'px-6 py-3 text-base'
}
return (
<button
className={clsx(
baseStyles,
variants[variant],
sizes[size],
(loading || disabled) && 'opacity-50 cursor-not-allowed',
className
)}
disabled={loading || disabled}
ref={ref}
{...props}
>
{loading && (
<svg className="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
{children}
</button>
)
}
)
Button.displayName = 'Button'
export { Button }
// 8. Shared Utilities Package (packages/utils/src/logger.ts)
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
export interface LogEntry {
level: LogLevel
message: string
timestamp: Date
metadata?: Record<string, any>
}
export class Logger {
private level: LogLevel = 'info'
setLevel(level: LogLevel) {
this.level = level
}
private shouldLog(level: LogLevel): boolean {
const levels: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3
}
return levels[level] >= levels[this.level]
}
private log(level: LogLevel, message: string, metadata?: Record<string, any>) {
if (!this.shouldLog(level)) return
const entry: LogEntry = {
level,
message,
timestamp: new Date(),
metadata
}
if (process.env.NODE_ENV === 'development') {
console[level](message, metadata || '')
} else {
// In production, you might want to send logs to a service
console.log(JSON.stringify(entry))
}
}
debug(message: string, metadata?: Record<string, any>) {
this.log('debug', message, metadata)
}
info(message: string, metadata?: Record<string, any>) {
this.log('info', message, metadata)
}
warn(message: string, metadata?: Record<string, any>) {
this.log('warn', message, metadata)
}
error(message: string, metadata?: Record<string, any>) {
this.log('error', message, metadata)
}
}
export const logger = new Logger()
// 9. Shared Types (packages/types/src/api.ts)
export interface ApiResponse<T = any> {
success: boolean
data?: T
message?: string
error?: string
timestamp?: string
}
export interface PaginatedResponse<T> extends ApiResponse<T[]> {
pagination: {
page: number
limit: number
total: number
totalPages: number
}
}
export interface ApiError {
code: string
message: string
field?: string
}
// 10. Shared Types (packages/types/src/user.ts)
export interface User {
id: number
name: string
email: string
avatar?: string
role: UserRole
createdAt: string
updatedAt: string
}
export type UserRole = 'admin' | 'user' | 'moderator'
export interface CreateUserRequest {
name: string
email: string
role?: UserRole
}
export interface UpdateUserRequest {
name?: string
email?: string
role?: UserRole
}
💻 Workflows Avancés Turbo yaml
🔴 complex
⭐⭐⭐⭐⭐
Workflows avancés Turbo incluant caching, filtering, deployment, et intégration CI/CD
⏱️ 45 min
🏷️ turbo, ci/cd, deployment, advanced
Prerequisites:
Advanced Turbo, Docker, GitHub Actions, CI/CD
// Turbo Advanced Workflows and CI/CD
// 1. Advanced turbo.json with complex dependencies
{
"$schema": "https://turbo.build/schema.json",
"globalEnv": [
"NODE_ENV",
"NEXT_PUBLIC_API_URL",
"DATABASE_URL",
"STRIPE_API_KEY",
"SENTRY_DSN"
],
"globalPassThroughEnv": [
"VERCEL_*",
"AWS_*",
"GITHUB_*"
],
"pipeline": {
"build": {
"dependsOn": ["^build", "^db:generate"],
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**",
"!.next/**/(!*.next)",
"public/build/**"
],
"env": ["NODE_ENV", "NEXT_PUBLIC_API_URL"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json"]
},
"db:generate": {
"outputs": ["prisma/migrations/**", "node_modules/.prisma/**"],
"inputs": ["prisma/schema.prisma", "package.json"]
},
"db:push": {
"cache": false,
"outputs": ["prisma/migrations/**"]
},
"db:migrate": {
"cache": false,
"outputs": ["prisma/migrations/**"]
},
"test": {
"dependsOn": ["build", "db:generate"],
"outputs": ["coverage/**", "test-results/**"],
"inputs": [
"src/**/*.tsx",
"src/**/*.ts",
"test/**/*.ts",
"test/**/*.tsx",
"jest.config.*"
],
"passThroughEnv": ["CI"]
},
"test:watch": {
"cache": false,
"persistent": true,
"dependsOn": ["build"]
},
"lint": {
"outputs": [],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "*.config.*"]
},
"lint:fix": {
"cache": false,
"outputs": []
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
},
"dev": {
"cache": false,
"persistent": true,
"dependsOn": ["db:generate"]
},
"clean": {
"cache": false,
"outputs": []
},
"format": {
"outputs": [],
"inputs": [
"**/*.{ts,tsx,js,jsx,json,md,css,scss}",
".prettierrc*"
]
},
"storybook": {
"cache": false,
"persistent": true,
"dependsOn": ["build"]
},
"build-storybook": {
"dependsOn": ["build"],
"outputs": ["storybook-static/**"]
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"cache": false
},
"size-limit": {
"dependsOn": ["build"],
"outputs": []
},
"bundlesize": {
"dependsOn": ["build"],
"outputs": []
}
}
}
// 2. GitHub Actions Workflow (.github/workflows/ci.yml)
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
NODE_VERSION: 20
PNPM_VERSION: 8
jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ runner.os }}-${{ matrix.node-version }}-${{ env.PNPM_VERSION }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-store-${{ runner.os }}-${{ matrix.node-version }}-${{ env.PNPM_VERSION }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate database schema
run: pnpm db:generate
- name: Run type checking
run: pnpm type-check
- name: Run linting
run: pnpm lint
- name: Run tests
run: pnpm test --coverage
- name: Build packages and apps
run: pnpm build
- name: Check bundle sizes
run: pnpm size-limit
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
lint-pr:
name: Lint Pull Request
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run changesets
uses: changesets/action@v1
with:
publish: pnpm changeset publish
commit: 'chore: release packages'
title: 'chore: release packages'
// 3. Deployment Script (scripts/deploy.sh)
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
ENVIRONMENT=${1:-production}
APP_NAME="my-turbo-app"
echo -e "${GREEN}Starting deployment of ${APP_NAME} to ${ENVIRONMENT}${NC}"
# Step 1: Run tests
echo -e "${YELLOW}Running tests...${NC}"
pnpm test
echo -e "${GREEN}✓ Tests passed${NC}"
# Step 2: Build all packages
echo -e "${YELLOW}Building packages...${NC}"
pnpm build
echo -e "${GREEN}✓ Build completed${NC}"
# Step 3: Deploy based on environment
case ${ENVIRONMENT} in
"production")
echo -e "${YELLOW}Deploying to production...${NC}"
# Deploy web app to Vercel
echo "Deploying web app to Vercel..."
cd apps/web
npx vercel --prod --token $VERCEL_TOKEN
cd ../..
# Deploy API to Railway/Heroku
echo "Deploying API to production..."
cd apps/api
# Railway deployment
railway deploy
cd ../..
echo -e "${GREEN}✓ Production deployment completed${NC}"
;;
"staging")
echo -e "${YELLOW}Deploying to staging...${NC}"
# Deploy web app to Vercel preview
cd apps/web
npx vercel --token $VERCEL_TOKEN
cd ../..
# Deploy API to staging
cd apps/api
# Staging deployment
docker build -t ${APP_NAME}-api:staging .
docker push ${DOCKER_REGISTRY}/${APP_NAME}-api:staging
cd ../..
echo -e "${GREEN}✓ Staging deployment completed${NC}"
;;
*)
echo -e "${RED}Unknown environment: ${ENVIRONMENT}${NC}"
exit 1
;;
esac
echo -e "${GREEN}🚀 Deployment completed successfully!${NC}"
// 4. Docker Compose for Development (docker-compose.dev.yml)
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: myapp_dev
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
api:
build:
context: .
dockerfile: apps/api/Dockerfile.dev
ports:
- "3001:3001"
environment:
NODE_ENV: development
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/myapp_dev
REDIS_URL: redis://redis:6379
depends_on:
- postgres
- redis
volumes:
- ./apps/api:/app/apps/api
- ./packages:/app/packages
- /app/node_modules
web:
build:
context: .
dockerfile: apps/web/Dockerfile.dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
NEXT_PUBLIC_API_URL: http://localhost:3001
depends_on:
- api
volumes:
- ./apps/web:/app/apps/web
- ./packages:/app/packages
- /app/node_modules
volumes:
postgres_data:
redis_data:
minio_data:
// 5. API Dockerfile for Development (apps/api/Dockerfile.dev)
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json ./apps/api/
COPY packages/*/package.json ./packages/*/
# Install pnpm
RUN npm install -g pnpm
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY apps/api ./apps/api
COPY packages ./packages
# Generate Prisma client
RUN pnpm db:generate
EXPOSE 3001
CMD ["pnpm", "dev"]
// 6. Web Dockerfile for Development (apps/web/Dockerfile.dev)
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/web/package.json ./apps/web/
COPY packages/*/package.json ./packages/*/
# Install pnpm
RUN npm install -g pnpm
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY apps/web ./apps/web
COPY packages ./packages
EXPOSE 3000
CMD ["pnpm", "dev"]
// 7. Size Limit Configuration (size-limit.json)
[
{
"name": "Web App Bundle",
"path": "apps/web/.next/static/chunks/**/*.js",
"limit": "1 MB",
"ignore": [
"**/node_modules/**"
]
},
{
"name": "API Bundle",
"path": "apps/api/dist/**/*.js",
"limit": "500 KB"
},
{
"name": "UI Components",
"path": "packages/ui/dist/**/*.js",
"limit": "100 KB"
},
{
"name": "Utils Package",
"path": "packages/utils/dist/**/*.js",
"limit": "50 KB"
}
]
// 8. Advanced Package Scripts
{
"scripts": {
// Development workflows
"dev:all": "concurrently "pnpm dev:web" "pnpm dev:api" "pnpm dev:storybook"",
"dev:web": "turbo run dev --filter=@repo/web",
"dev:api": "turbo run dev --filter=@repo/api",
"dev:storybook": "turbo run storybook --filter=@repo/ui",
// Build workflows
"build:affected": "turbo run build --filter='...[origin/HEAD]'",
"build:web": "turbo run build --filter=@repo/web",
"build:api": "turbo run build --filter=@repo/api",
// Test workflows
"test:affected": "turbo run test --filter='...[origin/HEAD]'",
"test:ci": "turbo run test --concurrency=1",
"test:coverage": "turbo run test -- --coverage",
// Quality assurance
"lint:affected": "turbo run lint --filter='...[origin/HEAD]'",
"type-check:affected": "turbo run type-check --filter='...[origin/HEAD]'",
"quality-check": "pnpm lint && pnpm type-check && pnpm test && pnpm build",
// Database workflows
"db:generate": "turbo run db:generate",
"db:push": "turbo run db:push",
"db:migrate": "turbo run db:migrate",
"db:reset": "pnpm db:push --force-reset",
// Package management
"clean": "turbo run clean && rm -rf node_modules",
"reinstall": "pnpm clean && pnpm install",
"update": "pnpm update -i -r && pnpm install",
// Release workflows
"changeset": "changeset",
"version": "changeset version",
"release": "pnpm build && pnpm test && changeset publish",
// Deployment workflows
"deploy:staging": "./scripts/deploy.sh staging",
"deploy:production": "./scripts/deploy.sh production",
// Performance monitoring
"size-limit": "size-limit",
"bundle-analyzer": "turbo run build --filter=@repo/web && npx @next/bundle-analyzer apps/web/.next"
}
}