🎯

monorepo-structure

🎯Skill

from dadbodgeoff/drift

VibeIndex|
What it does

Configures a Turborepo with pnpm to create a monorepo with shared code, types, and parallel builds across frontend, backend, and worker packages.

πŸ“¦

Part of

dadbodgeoff/drift(69 items)

monorepo-structure

Installation

pnpmRun with pnpm
pnpm install
πŸ“– Extracted from docs: dadbodgeoff/drift
4Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Set up a Turborepo + pnpm monorepo for sharing code between frontend, backend, and workers. One repo, multiple packages, shared types, parallel builds.

Overview

# Monorepo Structure

One repo, multiple packages, shared types, parallel builds.

When to Use This Skill

  • Sharing code between frontend and backend
  • Multiple apps need common types/utilities
  • Want atomic commits across packages
  • Tired of version hell with separate repos
  • Need parallel builds with caching

Core Concepts

  1. Workspaces - pnpm manages multiple packages in one repo
  2. Turborepo - Orchestrates builds with caching and parallelization
  3. Shared types - Single source of truth for TypeScript types
  4. Build order - Dependencies build before dependents

Project Structure

```

project-root/

β”œβ”€β”€ apps/

β”‚ β”œβ”€β”€ web/ # Next.js frontend

β”‚ β”‚ β”œβ”€β”€ app/

β”‚ β”‚ β”œβ”€β”€ components/

β”‚ β”‚ └── package.json

β”‚ β”œβ”€β”€ api/ # Backend API

β”‚ β”‚ β”œβ”€β”€ src/

β”‚ β”‚ └── package.json

β”‚ └── worker/ # Background worker

β”‚ β”œβ”€β”€ src/

β”‚ └── package.json

β”‚

β”œβ”€β”€ packages/

β”‚ β”œβ”€β”€ types/ # Shared TypeScript types

β”‚ β”‚ β”œβ”€β”€ src/

β”‚ β”‚ β”‚ β”œβ”€β”€ index.ts

β”‚ β”‚ β”‚ β”œβ”€β”€ user.ts

β”‚ β”‚ β”‚ └── schemas.ts

β”‚ β”‚ └── package.json

β”‚ β”œβ”€β”€ utils/ # Shared utilities

β”‚ β”‚ └── package.json

β”‚ └── config/ # Shared configs (eslint, tsconfig)

β”‚ └── package.json

β”‚

β”œβ”€β”€ package.json # Root package.json

β”œβ”€β”€ pnpm-workspace.yaml

β”œβ”€β”€ turbo.json

└── tsconfig.base.json

```

TypeScript Implementation

pnpm-workspace.yaml

```yaml

packages:

- "apps/*"

- "packages/*"

```

Root package.json

```json

{

"name": "my-saas",

"private": true,

"scripts": {

"dev": "turbo dev",

"build": "turbo build",

"test": "turbo test",

"lint": "turbo lint",

"typecheck": "turbo typecheck",

"clean": "turbo clean && rm -rf node_modules"

},

"devDependencies": {

"turbo": "^2.0.0",

"typescript": "^5.4.0"

},

"packageManager": "pnpm@9.0.0"

}

```

turbo.json

```json

{

"$schema": "https://turbo.build/schema.json",

"globalDependencies": ["*/.env.local"],

"tasks": {

"build": {

"dependsOn": ["^build"],

"outputs": ["dist/", ".next/", "!.next/cache/**"]

},

"dev": {

"cache": false,

"persistent": true

},

"test": {

"dependsOn": ["^build"]

},

"typecheck": {

"dependsOn": ["^build"]

},

"lint": {

"dependsOn": ["^build"]

},

"clean": {

"cache": false

}

}

}

```

tsconfig.base.json

```json

{

"compilerOptions": {

"target": "ES2022",

"lib": ["ES2022"],

"module": "ESNext",

"moduleResolution": "bundler",

"strict": true,

"noUncheckedIndexedAccess": true,

"noImplicitOverride": true,

"esModuleInterop": true,

"skipLibCheck": true,

"forceConsistentCasingInFileNames": true,

"declaration": true,

"declarationMap": true,

"sourceMap": true,

"resolveJsonModule": true,

"isolatedModules": true

}

}

```

Shared Types Package

```json

// packages/types/package.json

{

"name": "@myapp/types",

"version": "0.0.1",

"private": true,

"main": "./dist/index.js",

"types": "./dist/index.d.ts",

"exports": {

".": {

"types": "./dist/index.d.ts",

"import": "./dist/index.js"

}

},

"scripts": {

"build": "tsc",

"dev": "tsc --watch",

"typecheck": "tsc --noEmit"

},

"devDependencies": {

"typescript": "^5.4.0"

},

"dependencies": {

"zod": "^3.23.0"

}

}

```

```json

// packages/types/tsconfig.json

{

"extends": "../../tsconfig.base.json",

"compilerOptions": {

"outDir": "./dist",

"rootDir": "./src"

},

"include": ["src/*/"]

}

```

```typescript

// packages/types/src/index.ts

export * from './user';

export * from './schemas';

```

```typescript

// packages/types/src/user.ts

export interface User {

id: string;

email: string;

name: string;

role: 'admin' | 'user' | 'guest';

createdAt: Date;

}

export interface CreateUserInput {

email: string;

name: string;

role?: 'admin' | 'user' | 'guest';

}

```

```typescript

// packages/types/src/schemas.ts

import { z } from 'zod';

export const UserSchema = z.object({

id: z.string().uuid(),

email: z.string().email(),

name: z.string().min(1),

role: z.enum(['admin', 'user', 'guest']),

createdAt: z.coerce.date(),

});

export const CreateUserSchema = z.object({

email: z.string().email(),

name: z.string().min(1),

role: z.enum(['admin', 'user', 'guest']).default('user'),

});

```

App Package Using Shared Types

```json

// apps/web/package.json

{

"name": "@myapp/web",

"version": "0.0.1",

"private": true,

"scripts": {

"dev": "next dev",

"build": "next build",

"start": "next start"

},

"dependencies": {

"@myapp/types": "workspace:*",

"next": "^14.0.0",

"react": "^18.0.0"

}

}

```

```typescript

// apps/web/app/api/users/route.ts

import type { User, CreateUserInput } from '@myapp/types';

import { CreateUserSchema } from '@myapp/types';

export async function POST(request: Request) {

const body = await request.json();

// Validate with shared schema

const input = CreateUserSchema.parse(body);

// Create user...

const user: User = await createUser(input);

return Response.json(user);

}

```

Shared Utils Package

```json

// packages/utils/package.json

{

"name": "@myapp/utils",

"version": "0.0.1",

"private": true,

"main": "./dist/index.js",

"types": "./dist/index.d.ts",

"scripts": {

"build": "tsc",

"dev": "tsc --watch"

},

"devDependencies": {

"typescript": "^5.4.0"

}

}

```

```typescript

// packages/utils/src/index.ts

export function formatDate(date: Date): string {

return date.toISOString().split('T')[0];

}

export function slugify(text: string): string {

return text

.toLowerCase()

.replace(/[^\w\s-]/g, '')

.replace(/\s+/g, '-');

}

export function sleep(ms: number): Promise {

return new Promise(resolve => setTimeout(resolve, ms));

}

```

Common Commands

```bash

# Install all dependencies

pnpm install

# Run all dev servers in parallel

pnpm dev

# Build everything (respects dependency order)

pnpm build

# Run tests across all packages

pnpm test

# Add dependency to specific package

pnpm add zod --filter @myapp/types

# Add dev dependency to root

pnpm add -D prettier -w

# Run command in specific package

pnpm --filter @myapp/web dev

# Run command in all packages matching pattern

pnpm --filter "@myapp/*" build

```

Dependency Flow

```

packages/types (source of truth)

↓

packages/utils (may import types)

↓

apps/web, apps/api, apps/worker (import both)

```

Turborepo handles build order via dependsOn: ["^build"] - packages always build before apps that depend on them.

.gitignore

```gitignore

# Dependencies

node_modules/

# Build outputs

dist/

.next/

.turbo/

# Environment

.env

.env.local

.env.*.local

# IDE

.idea/

.vscode/

# OS

.DS_Store

```

Best Practices

  1. *Use workspace:** - Always for internal dependencies
  2. Types flow down - Shared types package is the source of truth
  3. One tsconfig.base - Extend from root, override only what's needed
  4. Atomic commits - Change types and consumers in same commit
  5. Cache builds - Turborepo caches unchanged packages

Common Mistakes

  • Using ^1.0.0 instead of workspace:* for internal deps
  • Building packages individually instead of turbo build
  • Circular dependencies between packages
  • Not including dist/ in .gitignore
  • Forgetting dependsOn: ["^build"] in turbo.json

Related Skills

  • [TypeScript Strict](../typescript-strict/)
  • [Environment Config](../environment-config/)

More from this repository10

🎯
feature-flags🎯Skill

Enables controlled feature rollouts, A/B testing, and selective feature access through configurable flags for gradual deployment and user targeting.

🎯
design-tokens🎯Skill

Generates a comprehensive, type-safe design token system with WCAG AA color compliance and multi-framework support for consistent visual design.

🎯
file-uploads🎯Skill

Securely validates, scans, and processes file uploads with multi-stage checks, malware detection, and race condition prevention.

🎯
ai-coaching🎯Skill

Guides users through articulating creative intent by extracting structured parameters and detecting conversation readiness.

🎯
environment-config🎯Skill

Validates and centralizes environment variables with type safety, fail-fast startup checks, and multi-environment support.

🎯
community-feed🎯Skill

Generates efficient social feed with cursor pagination, trending algorithms, and engagement tracking for infinite scroll experiences.

🎯
cloud-storage🎯Skill

Enables secure, multi-tenant cloud file storage with signed URLs, direct uploads, and visibility control for user-uploaded assets.

🎯
email-service🎯Skill

Simplifies email sending, templating, and tracking with robust SMTP integration and support for multiple email providers and transactional workflows.

🎯
error-sanitization🎯Skill

Sanitizes error messages by logging full details server-side while exposing only generic, safe messages to prevent sensitive information leakage.

🎯
batch-processing🎯Skill

Optimizes database operations by collecting and batching independent records, improving throughput by 30-40% with built-in fallback processing.