NX โ
NX is a powerful build system and developer toolkit for managing modern monorepos. Think of it as the orchistrator that helps organize, build, test, and deploy all of our projects โ without losing sanity.
It provides:
- โก Fast builds (via smart caching and task graphing)
- ๐งฑ Modular architecture (great for microservices and reusable packages)
- ๐งช Built-in testing, linting, formatting
- ๐ Affected-based tooling (only test/build/deploy what actually changed)
- ๐ Project-aware CLI & IDE integration
- ๐ง Plugin ecosystem for things like Firebase, Vite, Nuxt, etc.
Terminology โ
- Workspace: Refers the the monorepo itself.
- Project: Refers to each individual project in the repo. (any folder with a package.json)
- Target: A target is any
package.jsonorproject.jsonscript that can be run within a project. - Executor: A binary used by
NXto make changes to a project. For instance running arbitrary cli commands or migrating a package to a new version.
Project Setup โ
[todo] Projects can be created in several ways using nx.
Each project should contian a project.json for nx to pickup. This is used to build the project and target dependency graph.
project.json โ
Using project.json in the root of each proejct we can leverage nx to make working with our apps simple. Using the nx cli task runner we can execute jobs on our apps.
nx run ...scriptnameThis will spin up a terminal instace for this command. The real value comes when running a job with mutiple dependencies.
For example:
// myapp/project.json
{
"name": "@services/gateway",
"root": "core/service-auth",
"tags": ["service"],
"targets": {
"emulators": {
"executor": "nx:run-commands",
"continuous": true,
"forwardAllArgs": true,
"options": {
"cwd": "core/service-auth",
"commands": [
{
"command": "npm run emulators"
}
]
}
},
"dev": {
"executor": "nx:run-commands",
"options": {
"commands": [{ "command": "sleep 5 && npm run dev" }],
"cwd": "core/service-auth"
},
"dependsOn": ["emulators"]
},
"deploy": {
"executor": "nx:run-commands",
"options": {
"cwd": "core/service-auth",
"commands": [{ "command": "firebase deploy" }]
},
"dependsOn": ["test:once", "build"]
}
},
"build": {
"executor": "@nx/js:tsc"
}
}This project.json does 2 things. It specifies the dev server setup and the deploy pipeline.
nx devBy running this command NX will check for dependencies of the dev target and will find the firebase emulators script. It will then spin up both the emulator suite and the project dev server.
In this example we also specify the "deploy" target. This depends on the "test:once" target. If "test:once" fails the deploy will not complete. This is how our CI pipeline will know what to do when the time comes.
Maintaining this sort of setup for each of our projects make for smooth pickup and play of any project in the repo.
Project Minimal Setup โ
Just to show what is required by NX to work smoothly this is the bare bones it requires.
my-project/
โ # ...
โโโ project.jsonproject.json is just like a supercharged package.json. Where you can specify in detail what targets depend on what.
So firing up a project for nx is just adding the project.json and giving the thing some boilerplate:
{
"name": "@client/app-name", // package name
"root": "clients/app-name" // path from monorepo root.
}