Course:Node.js & Express/
Lesson

At some point a project outgrows a single package. You have a backend, a frontend, and a shared utilities library, and you are copy-pasting types between them. A monorepoWhat is monorepo?A single repository that contains multiple related projects or packages, managed together with shared tooling. keeps all of that code in one repositoryWhat is repository?A project folder tracked by Git that stores your files along with the complete history of every change, inside a hidden .git directory., and npm Workspaces handles the plumbing: shared node_modules, local cross-package references, and commands you can run across every package at once.

What a monorepoWhat is monorepo?A single repository that contains multiple related projects or packages, managed together with shared tooling. looks like

It is one git repositoryWhat is repository?A project folder tracked by Git that stores your files along with the complete history of every change, inside a hidden .git directory. containing several package.json files. The root package.json declares the workspaces; everything else is a regular package that happens to live inside the same repo.

monorepo/
├── package.json          # Root - declares workspaces
├── node_modules/         # All shared dependencies live here
├── packages/
│   ├── shared/           # Utility library
│   │   ├── package.json
│   │   └── src/
│   ├── api/              # Express backend
│   │   ├── package.json
│   │   └── src/
│   └── web/              # React frontend
│       ├── package.json
│       └── src/
└── apps/
    └── admin/
        └── package.json
02

Setting up workspaces

The root package.json points to the directories that contain packages. Glob patterns keep this concise:

json
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces --if-present",
    "test": "npm run test --workspaces --if-present",
    "dev:api": "npm run dev -w @myorg/api",
    "dev:web": "npm run dev -w @myorg/web"
  }
}

"private": true is not optional, without it, you might accidentally publish the root to the npm registryWhat is registry?A server that stores and distributes packages or container images - npm registry for JavaScript packages, Docker Hub for container images..

The --if-present flag is important when you run a script across all workspaces. Without it, npm throws an error for any package that does not define that particular script.
03

Individual package configuration

Each package inside the workspace has its own package.json with a scoped name:

json
// packages/shared/package.json
{
  "name": "@myorg/shared",
  "version": "1.0.0",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "test": "jest"
  }
}

Other packages can depend on it using its package name:

json
// packages/api/package.json
{
  "name": "@myorg/api",
  "dependencies": {
    "@myorg/shared": "^1.0.0",
    "express": "^4.18.0"
  }
}

When you run npm install from the root, npm detects that @myorg/shared is a local workspace package and creates a symlink rather than downloading from the registryWhat is registry?A server that stores and distributes packages or container images - npm registry for JavaScript packages, Docker Hub for container images.:

node_modules/
├── @myorg/shared  →  ../../packages/shared   (symlink)
├── @myorg/api     →  ../../packages/api       (symlink)
└── express/

This means changes you make to packages/shared/src are immediately visible to the APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. and web packages, no publish step required.

04

Running workspace commands

# Install everything (run from root)
npm install

# Run a script in a specific workspace
npm run build -w @myorg/shared

# Run a script in all workspaces
npm run test --workspaces --if-present

# Add a dependency to one workspace
npm install lodash -w @myorg/api

# Add a dependency to all workspaces
npm install typescript -ws

# Add a dev dependency to one workspace
npm install -D jest -w @myorg/shared
05

Cross-package dependencies in practice

The real power comes when packages start importing from each other. Once the symlink is in place, you write ordinary import statements:

// In packages/api/src/index.ts
import { formatDate } from '@myorg/shared';

Your TypeScript compilerWhat is compiler?A program that translates code you write into a language your computer can execute. It also catches errors before your code runs. and bundler follow the symlink transparently. You write code as if @myorg/shared were a published package, but it reads directly from your local packages/shared/src.

06

Publishing workspaces

When you are ready to publish packages to the npm registryWhat is registry?A server that stores and distributes packages or container images - npm registry for JavaScript packages, Docker Hub for container images., you publish per-package, not the root:

# Bump version in one workspace
npm version patch -w @myorg/shared

# Publish it
npm publish -w @myorg/shared --access public

For projects that publish many packages together, @changesets/cli is the standard tool. It tracks which packages changed, coordinates version bumps across interdependent packages, and generates changelogs:

npm install --save-dev @changesets/cli
npx changeset init
json
{
  "scripts": {
    "changeset": "changeset",
    "version-packages": "changeset version",
    "release": "npm run build --workspaces && changeset publish"
  }
}
07

Choosing a monorepoWhat is monorepo?A single repository that contains multiple related projects or packages, managed together with shared tooling. tool

npm Workspaces handles the basics well. For more complex needs, other tools layer on top:

ToolBest forComplexity
npm Workspaces2-10 related packages, simple buildsLow
TurborepoBuild caching, large reposMedium
NxEnterprise scale, code generationHigh
LernaPublishing + versioning automationMedium
pnpm WorkspacesDisk efficiency, strict isolationLow

If you have fewer than 10 packages and a straightforward build process, npm Workspaces alone is the right choice. Reach for Turborepo when slow builds start hurting your team's productivity, its caching layer can dramatically cut CI times.