nx-workspace-patterns · diff
git:20260307.47a5dbc to git:20260522.be57c0b
2 added, 384 removed. Audit A to A.
---
name: nx-workspace-patterns
description: Configure and optimize Nx monorepo workspaces. Use when setting up Nx, configuring project boundaries, optimizing build caching, or implementing affected commands.
---
# Nx Workspace Patterns
Production patterns for Nx monorepo management.
## When to Use This Skill
- Setting up new Nx workspaces
- Configuring project boundaries
- Optimizing CI with affected commands
- Implementing remote caching
- Managing dependencies between projects
- Migrating to Nx
## Core Concepts
### 1. Nx Architecture
```
workspace/
├── apps/ # Deployable applications
│ ├── web/
│ └── api/
├── libs/ # Shared libraries
│ ├── shared/
│ │ ├── ui/
│ │ └── utils/
│ └── feature/
│ ├── auth/
│ └── dashboard/
├── tools/ # Custom executors/generators
├── nx.json # Nx configuration
└── workspace.json # Project configuration
```
### 2. Library Types
| Type | Purpose | Example |
| --------------- | -------------------------------- | ------------------- |
| **feature** | Smart components, business logic | `feature-auth` |
| **ui** | Presentational components | `ui-buttons` |
| **data-access** | API calls, state management | `data-access-users` |
| **util** | Pure functions, helpers | `util-formatting` |
| **shell** | App bootstrapping | `shell-web` |
- ## Templates
-
- ### Template 1: nx.json Configuration
-
- ```json
- {
- "$schema": "./node_modules/nx/schemas/nx-schema.json",
- "npmScope": "myorg",
- "affected": {
- "defaultBase": "main"
- },
- "tasksRunnerOptions": {
- "default": {
- "runner": "nx/tasks-runners/default",
- "options": {
- "cacheableOperations": [
- "build",
- "lint",
- "test",
- "e2e",
- "build-storybook"
- ],
- "parallel": 3
- }
- }
- },
- "targetDefaults": {
- "build": {
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"],
- "cache": true
- },
- "test": {
- "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
- "cache": true
- },
- "lint": {
- "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
- "cache": true
- },
- "e2e": {
- "inputs": ["default", "^production"],
- "cache": true
- }
- },
- "namedInputs": {
- "default": ["{projectRoot}/**/*", "sharedGlobals"],
- "production": [
- "default",
- "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
- "!{projectRoot}/tsconfig.spec.json",
- "!{projectRoot}/jest.config.[jt]s",
- "!{projectRoot}/.eslintrc.json"
- ],
- "sharedGlobals": [
- "{workspaceRoot}/babel.config.json",
- "{workspaceRoot}/tsconfig.base.json"
- ]
- },
- "generators": {
- "@nx/react": {
- "application": {
- "style": "css",
- "linter": "eslint",
- "bundler": "webpack"
- },
- "library": {
- "style": "css",
- "linter": "eslint"
- },
- "component": {
- "style": "css"
- }
- }
- }
- }
- ```
-
- ### Template 2: Project Configuration
-
- ```json
- // apps/web/project.json
- {
- "name": "web",
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "apps/web/src",
- "projectType": "application",
- "tags": ["type:app", "scope:web"],
- "targets": {
- "build": {
- "executor": "@nx/webpack:webpack",
- "outputs": ["{options.outputPath}"],
- "defaultConfiguration": "production",
- "options": {
- "compiler": "babel",
- "outputPath": "dist/apps/web",
- "index": "apps/web/src/index.html",
- "main": "apps/web/src/main.tsx",
- "tsConfig": "apps/web/tsconfig.app.json",
- "assets": ["apps/web/src/assets"],
- "styles": ["apps/web/src/styles.css"]
- },
- "configurations": {
- "development": {
- "extractLicenses": false,
- "optimization": false,
- "sourceMap": true
- },
- "production": {
- "optimization": true,
- "outputHashing": "all",
- "sourceMap": false,
- "extractLicenses": true
- }
- }
- },
- "serve": {
- "executor": "@nx/webpack:dev-server",
- "defaultConfiguration": "development",
- "options": {
- "buildTarget": "web:build"
- },
- "configurations": {
- "development": {
- "buildTarget": "web:build:development"
- },
- "production": {
- "buildTarget": "web:build:production"
- }
- }
- },
- "test": {
- "executor": "@nx/jest:jest",
- "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
- "options": {
- "jestConfig": "apps/web/jest.config.ts",
- "passWithNoTests": true
- }
- },
- "lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"],
- "options": {
- "lintFilePatterns": ["apps/web/**/*.{ts,tsx,js,jsx}"]
- }
- }
- }
- }
- ```
-
- ### Template 3: Module Boundary Rules
-
- ```json
- // .eslintrc.json
- {
- "root": true,
- "ignorePatterns": ["**/*"],
- "plugins": ["@nx"],
- "overrides": [
- {
- "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
- "rules": {
- "@nx/enforce-module-boundaries": [
- "error",
- {
- "enforceBuildableLibDependency": true,
- "allow": [],
- "depConstraints": [
- {
- "sourceTag": "type:app",
- "onlyDependOnLibsWithTags": [
- "type:feature",
- "type:ui",
- "type:data-access",
- "type:util"
- ]
- },
- {
- "sourceTag": "type:feature",
- "onlyDependOnLibsWithTags": [
- "type:ui",
- "type:data-access",
- "type:util"
- ]
- },
- {
- "sourceTag": "type:ui",
- "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
- },
- {
- "sourceTag": "type:data-access",
- "onlyDependOnLibsWithTags": ["type:data-access", "type:util"]
- },
- {
- "sourceTag": "type:util",
- "onlyDependOnLibsWithTags": ["type:util"]
- },
- {
- "sourceTag": "scope:web",
- "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"]
- },
- {
- "sourceTag": "scope:api",
- "onlyDependOnLibsWithTags": ["scope:api", "scope:shared"]
- },
- {
- "sourceTag": "scope:shared",
- "onlyDependOnLibsWithTags": ["scope:shared"]
- }
- ]
- }
- ]
- }
- }
- ]
- }
- ```
-
- ### Template 4: Custom Generator
-
- ```typescript
- // tools/generators/feature-lib/index.ts
- import {
- Tree,
- formatFiles,
- generateFiles,
- joinPathFragments,
- names,
- readProjectConfiguration,
- } from "@nx/devkit";
- import { libraryGenerator } from "@nx/react";
-
- interface FeatureLibraryGeneratorSchema {
- name: string;
- scope: string;
- directory?: string;
- }
-
- export default async function featureLibraryGenerator(
- tree: Tree,
- options: FeatureLibraryGeneratorSchema,
- ) {
- const { name, scope, directory } = options;
- const projectDirectory = directory
- ? `${directory}/${name}`
- : `libs/${scope}/feature-${name}`;
-
- // Generate base library
- await libraryGenerator(tree, {
- name: `feature-${name}`,
- directory: projectDirectory,
- tags: `type:feature,scope:${scope}`,
- style: "css",
- skipTsConfig: false,
- skipFormat: true,
- unitTestRunner: "jest",
- linter: "eslint",
- });
-
- // Add custom files
- const projectConfig = readProjectConfiguration(
- tree,
- `${scope}-feature-${name}`,
- );
- const projectNames = names(name);
-
- generateFiles(
- tree,
- joinPathFragments(__dirname, "files"),
- projectConfig.sourceRoot,
- {
- ...projectNames,
- scope,
- tmpl: "",
- },
- );
-
- await formatFiles(tree);
- }
- ```
-
- ### Template 5: CI Configuration with Affected
-
- ```yaml
- # .github/workflows/ci.yml
- name: CI
-
- on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
- env:
- NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
-
- jobs:
- main:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: "npm"
-
- - name: Install dependencies
- run: npm ci
-
- - name: Derive SHAs for affected commands
- uses: nrwl/nx-set-shas@v4
-
- - name: Run affected lint
- run: npx nx affected -t lint --parallel=3
-
- - name: Run affected test
- run: npx nx affected -t test --parallel=3 --configuration=ci
-
- - name: Run affected build
- run: npx nx affected -t build --parallel=3
-
- - name: Run affected e2e
- run: npx nx affected -t e2e --parallel=1
- ```
-
- ### Template 6: Remote Caching Setup
-
- ```typescript
- // nx.json with Nx Cloud
- {
- "tasksRunnerOptions": {
- "default": {
- "runner": "nx-cloud",
- "options": {
- "cacheableOperations": ["build", "lint", "test", "e2e"],
- "accessToken": "your-nx-cloud-token",
- "parallel": 3,
- "cacheDirectory": ".nx/cache"
- }
- }
- },
- "nxCloudAccessToken": "your-nx-cloud-token"
- }
-
- // Self-hosted cache with S3
- {
- "tasksRunnerOptions": {
- "default": {
- "runner": "@nx-aws-cache/nx-aws-cache",
- "options": {
- "cacheableOperations": ["build", "lint", "test"],
- "awsRegion": "us-east-1",
- "awsBucket": "my-nx-cache-bucket",
- "awsProfile": "default"
- }
- }
- }
- }
- ```
-
- ## Common Commands
-
- ```bash
- # Generate new library
- nx g @nx/react:lib feature-auth --directory=libs/web --tags=type:feature,scope:web
-
- # Run affected tests
- nx affected -t test --base=main
-
- # View dependency graph
- nx graph
-
- # Run specific project
- nx build web --configuration=production
-
- # Reset cache
- nx reset
+ ## Templates and detailed worked examples
- # Run migrations
- nx migrate latest
- nx migrate --run-migrations
- ```
+ Full template library and detailed worked examples live in `references/details.md`. Read that file when you need the concrete templates.
## Best Practices
### Do's
- **Use tags consistently** - Enforce with module boundaries
- **Enable caching early** - Significant CI savings
- **Keep libs focused** - Single responsibility
- **Use generators** - Ensure consistency
- **Document boundaries** - Help new developers
### Don'ts
- **Don't create circular deps** - Graph should be acyclic
- **Don't skip affected** - Test only what changed
- **Don't ignore boundaries** - Tech debt accumulates
- **Don't over-granularize** - Balance lib count