We have talked previously about creating a multi-package typesript project using only node’s workspace feature. This could be considered as a monorepo where you’d have an application with some libraries. However, if you want to scale up, this simple setup may not be enough.
That’s why today I am looking into Nx, which is a tool that helps you build and manage your monorepo! It’s js based (written in typescript), but it can also take care of multiple languages like Kotlin, Go, and Python.
I have come across the Nx monorepo tools website which explains the reasoning behind using monorepo and, enumerates the different tools available for monorepo management. So check it out, for more information! (though since it’s from Nx, you might be biased into using it).
Setup
Install Nx with the following command:
npm install -g nx
It needs to be installed globally or the CLI might get stuck. You may need other plugins for different languages, but they don’t need to be installed globally and can be added once the monorepo is created.
Creating the Monorepo
Create a new workspace with the following command (using yarn for the monorepo installation).
You can use --preset=empty
if you don’t want any plugins installed.
npx create-nx-workspace@latest --pm yarn
In my case, this monorepo will need a frontend, so I don’t mind starting by setting the out-of-the-box typescript project.
By default, Nx creates the monorepo in a subfolder, this will be the name of the workspace. Then you can set the app’s name you want like frontend if we are creative 🙃.
Finally, you must set Integrated Monorepo
when asked the project kind:
$ Integrated monorepo, or standalone project? …
Integrated_Monorepo: Nx creates a monorepo that contains multiple projects.
Standalone: Nx creates a single project and makes it fast.
This will set up your project as a monorepo, otherwise it will just be a Nx standalone project.
Adding a new project
If you are having an issue, check out the common problem section of the article to help you out!
Adding the backend
Now that you have the monorepo setup, we can add a new project to it.
Since my backend will not be in typescript but go, I will add the nx-go
dependency to the project.
Though we could use yarn
to add the dependency, the nx
command is preferred as it also configures the nx.json
which has the project configuration.
nx add @nx-go/nx-go --save-dev
This happens at the root of the monorepo and thus is not mixed with my frontend project. Then you can create a new project with the following command:
nx g @nx-go/nx-go:application backend --directory apps/backend
Now we should have out monorepo setup with the frontend and backend projects.
.
├── README.md
├── apps
| ├── backend
| └── frontend
├── node_modules
├── nx.json
├── package.json
└── yarn.lock
Depending on the framework you use, you might have more or less files at the base, but the structure should be similar.
There are no libraries at the moment, but they could be added in a libs
folder using nx g lib
.
Adding the e2e
If you used the create-nx-workspace
command,
you might already have been prompted with creating an e2e project for the frontend.
But let’s review how to do it manually, because it is not as straightforward!
To add the e2e project, find the framework you want to use, for example playwright and cypress have both plugins for Nx. Since I am familiar with cypress I will add it to the monorepo, starting by its plugin.
nx add @nx/cypress --dev
Now this plugin has two generators that could be interesting:
- configuration : Add a Cypress E2E Configuration to an existing project.
- Will configure a project to be an e2e project.
- component-configuration : Set up Cypress Component Test for a project
- Will add a cypress folder within a project.
So if I want to create a new e2e
project for the frontend, I would do the following:
- Create a new project folder in
apps
callede2e
- Add a
project.json
file in thee2e
folder with the following content:{ "root": "apps/e2e", "sourceRoot": "apps/e2e/src", "projectType": "application", "implicitDependencies": ["frontend"] }
- Run the following command to add the e2e project:
nx generate @nx/cypress:configuration --project e2e --devServerTarget=frontend:dev --baseUrl=http://localhost:3000
- The project
e2e
is the one you created - The devServerTarget is the command to serve the frontend.
- The baseUrl is the url where the frontend is served.
- The project
It will ask you for a bundler, I chose the same as the frontend for consistency.
In the cypress.config.ts
created,
you can add more webServerCommands
to run the e2e tests as well as CI specific commands.
Command your Nx monorepo
Now that it is all setup, you might want to try and test things out to see if indeed the configuration is correct.
So let’s review the command that will be useful for your development process.
They are all meant to be run from the root, using nx
as the command.
Those commands are mapped (thanks to the plugins) and defined in the nx.json
file at the root of the monorepo.
Run an application
Here is how you would run the frontend:
# start the frontend in dev mode
nx dev frontend
# start the frontend in production mode
nx build frontend
nx start frontend
Run the tests
For the unit tests, you can run the following command:
nx test frontend
And for the e2e cypress tests we added you can use:
nx e2e e2e
In our case the project’s name is e2e
so it looks a bit funny.
Although it’s called by default frontend-e2e
when using the create workspace command,
I wanted to e2e test my backend within the same project.
But there might be a better option or better names!
Common problems
Here are some problems you might face and how to solve them. Most of the time I referred to the nx-recipe which provides working example of different nx configuration. So you can always compare what you are trying to do and how it was set up there for help.
Nx unresolved error
[ NX ] Unable to resolve @nx/plugin:generator
This could be that the plugin is not installed, so make sure you run:
yarn add @nx/plugin --dev
Or that the generator (or whichever) does not exist, so either you have made a typo or it’s called differently. But with AI-generated answers, it could be that the command you are trying is totally wrong. So use this command to list the action you can do:
nx list @nx/plugin
Then you will see the generators, executors and so on of that builder with a small description.
For example here, it could be that you had to use generate-project
and not generator
.
[ NX ] Capabilities in @nx/plugin:
GENERATORS
generate-project : Create a [Plugin] based project
Nx read property error
[ NX ] Cannot read properties of null (reading ‘length’)
I encountered this error because I was trying to use the configuration generator from the cypress plugin, before creating the project. Some plugins create the project for you some don’t, and the documentation makes it ambiguous for a newcomer.
This usually means that there’s a missing configuration in the project.json
of the target project.
Nx failed to process project graph
[ NX ] Failed to process project graph. Run “nx reset” to fix this. Please report the issue if you keep seeing it. See errors below.
This could be for multiple reasons:
- You have made a typo in the project configuration.
- You passed the wrong attributes to a plugin, and it failed to process the project.
- There is a typo in the
nx.json
file which defines the monorepo at the root. - You have a circular dependency in your project (both projects depend on each other).