Setting up npm workspaces from scratch
In a university project we had the challenge to combine multiple microservices in one GitHub repository. This challenge is effectively solved by npm workspaces.
Why?
I'm going to show you how to set this tool up and how to use it. But first, lets discuss why we'd want to do that. Because having all code in one place is advantageous. You will know what your team is working on and you avoid dependency hell. This is not making your monorepo be monolithic. You rather manage multiple indiviually deployable microservices in one repository.
Setting up npm workspaces
We will start with an empty git repository and will first initalize it as an npm package. Just run the following command and answer all questions:
npm init
You should end up with a package.json
file that looks somewhat similar to this:
{
"name": "npm workspaces demo",
"version": "0.0.1",
"description": "Source Code for demoing the set up of npm workspaces",
}
Create the module structure for the different npm workspaces
Next lets create the module structure that we imagine for our project. I like to keep all modules neatly organized in a packages
folder.
mkdir -p packages/package-a
mkdir package-b
To now initialize our workspaces we need to extend the package.json
like so:
{
"name": "npm workspaces demo",
"version": "0.0.1",
"description": "Source Code for demoing the set up of npm workspaces",
"workspaces": [
"packages/*"
],
}
Initialize the workspaces
For npm to understand that the subfolders in packages
are not just folders, but actual workspaces, we need to let npm know.
Adding a package.json
to each subfolder is key here. Create a package.json
-file in each package-folder, with minimal content. Make sure to name
the packages accordingly. You can choose the name freely, but remember to not name your package the same way a dependency is called. So if you
depende on the npm package graphql in one of the modules, none of your modules should be called graphql.
You can solve this by choosing another
name or scoping your package, like explained in detail here.
{
"name": "package-a",
"private": "true",
"version": "1.0.0"
}
Don't forget to create a package.json
file for package-b!
You can automate workspace creation with npm init -w packages/workspace
, which will
query the usual package.json
info and create necessary folders, as well as update your
root package.json
.
Your setup of npm workspaces is now complete. Lets check out how to work with this.
Managing dependencies
Installing all dependencies is super easy. Just run npm install
on the root level of your project
and npm will automatically install global dependencies which are specified in the root package.json
like prettier
and all the dependencies for each module. During this installation npm lifts the dependencies up to the root
folder if possible. So if two workspaces require different versions of a dependency, each workspace gets
their own node_modules
folder with their version of the dependency.
Adding a dependency like graphql to a single workspace can be done via
npm install graphql -w packages/package-a
.
This will only add graphql to package-a.
Use your workspaces code in another workspace
Consuming another workspaces code is totally straightforward. Since npm symlinks all workspaces to the root
node_modules
folder, we can just require contents within our code and use them.
There is no need to add package-a as a dependency for package-b! This is due to the way
node handles module resolution.
You now know everything you need to know to get started with npm workspaces. I hope I was able to help you understand npm workspaces better.