Monorepo with JavaScript: Organizing Code in Practice
\n\nDeveloping complex projects in JavaScript (Node.js) often faces the problem of code management. When you have several packages, libraries, or microservices, each living in its own repository, this leads to code duplication, complexity of version synchronization, and slowdown of CI/CD. The solution is a monorepo. In this article, we will analyze how to organize a monorepo with JavaScript in practice: from choosing tools to setting up build and publishing.
\n\nWhat is a monorepo and why is it needed?
\n\nA monorepo is an approach to code management where all packages, libraries, and applications of a project are stored in a single repository. Unlike multi-repositories (each package in its own repo), a monorepo offers:
\n\n- \n
- Unified change history: easily track how changes in one part affect another. \n
- Code reuse: shared utilities and components live in one place without publishing to npm. \n
- Simplified refactoring: changing the API of one package is immediately visible in all dependents. \n
- Unified CI/CD settings: one configuration file for all packages. \n
For JavaScript (Node.js), a monorepo is especially relevant as it allows you to get rid of "dependency hell" and speed up development. Let's look at a practical implementation.
\n\nTools for a monorepo in JavaScript
\n\nThere are several popular tools on the market. We will consider two main ones: npm workspaces (built into npm) and Lerna (extended functionality).
1. npm workspaces (built-in support)
\n\nStarting from npm v7, the package manager supports workspaces. This is the simplest way to start. Example project structure:
\n\nmy-monorepo/\n├── packages/\n│ ├── core/\n│ │ ├── package.json\n│ │ └── index.js\n│ ├── utils/\n│ │ ├── package.json\n│ │ └── helpers.js\n│ └── app/\n│ ├── package.json\n│ └── server.js\n├── package.json\n└── lerna.json (optional)\n\nRoot package.json:
{\n "name": "my-monorepo",\n "private": true,\n "workspaces": ["packages/*"]\n}\n\nNow all packages in packages/* are automatically linked. To install dependencies, simply run npm install in the root. Dependencies between packages are specified standardly:
// packages/app/package.json\n{\n "name": "@my/app",\n "dependencies": {\n "@my/core": "*",\n "@my/utils": "*"\n }\n}\n\nnpm workspaces creates symlinks in the node_modules folder, so imports work like with regular packages.
2. Lerna (advanced management)
\n\nLerna is a tool for managing monorepos with the ability to publish, version, and run scripts in parallel. Installation:
\n\nnpm install --global lerna\n\nInitialization in the project:
\n\nlerna init\n\nlerna.json file:
{\n "version": "independent",\n "npmClient": "npm",\n "packages": ["packages/*"]\n}\n\nThe independent mode allows each package to have its own version. Main commands:
- \n
lerna bootstrap— installs dependencies and links packages. \n lerna run build— runs thebuildscript in all packages. \n lerna publish— publishes changed packages to npm. \n
Practical code organization
\n\nLet's look at an example of setting up a monorepo for a web application with a Node.js backend and shared utilities.
\n\nStep 1: Basic structure
\n\nLet's create three packages:
\n\nBlogs
Book Recommendations
Read also
Deploying a TypeScript Application on VPS: A Step-by-Step Guide