Introduction to building an Angular app with Nx Workspace
Nx Workspace is a tool suite designed to architect, build and manage monorepos at any scale. It has out-of-the-box support for multiple frontend frameworks like Angular and React as well as backend technologies including Nest, Next, and Express. In this article, we will focus on building a workspace for an Angular-based project.
Monorepo fundamentals
The most basic definition of a monorepo is that it is a single repository that consists of multiple applications and libraries. This all is accompanied by a set of tooling, which enables us to work with those projects. This approach has several benefits including:
- shared code - it enables us to share code across the whole company or organization. This can result in code that is more DRY as we can reuse the common patterns, components, and types. This enables to share the logic between frontend and backend as well.
- atomic changes - without the monorepo approach, whenever we need to make a change that will affect multiple projects, we might need to coordinate those changes across multiple repositories, and possibly by multiple teams. For example, an API change might need to be reflected both on a server app and a client app. With monorepo, all of those changes can be applied in one commit on one repository, which greatly limits the coordination efforts necessary
- developer mobility - with a monorepo approach we get one consistent way of performing similar tasks even when using multiple technologies. The developers can now contribute to other teams' projects, and make sure that their changes are safe across the whole organization.
- single set of dependencies - By using a single repository with one set of dependencies, we make sure that our whole codebase depends on one single version of the given dependency. This way, there are no version conflicts between libraries. It is also less likely that the less used part of the repository will be left with an obsolete dependency because it will be updated along the way when other parts of the repository do this update.
If you want to read more about monorepos, here are some useful links:
- (Monorepo in Git)[https://www.atlassian.com/git/tutorials/monorepos]
- (Monorepo != monolith)[https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c]
- (Nrwl Nx Resources)[https://nx.dev/latest/angular/getting-started/resources]
Create a new workspace
With all that said about the monorepo, how do we actually create one using Nx Workspace and Angular? Just like with Angular CLI, there is an Nx CLI that does all the heavy lifting for us. With the following command, we can create a new workspace that leverages all of the aforementioned benefits of a monorepo:
npx create-nx-workspace --preset=angular
The tool will ask for a project name, stylesheet format, and linting tool. For the linting, I recommend using ESLint, which is a more modern tool. The CLI will also ask whether we want to use Nx Cloud in our workspace. We can opt-out from this for now as we can easily add that later on. After the command finishes, we end up with a brand new project all set up. Let's start by analyzing what has been generated for us.
Nx uses certain toolset by default:
- Jest for testing (instead of Karma and Jasmine)
- Cypress for e2e testing (instead of Protractor)
- ESLint for linting (instead of TSLint) in case you decide to use it when creating a workspace
All of these are modern tools, and I recommend sticking to them as they provide very good developer experiences, and are actively maintained.
The base structure that is created for us looks as follows:
- apps/
- {{appName}}
- {{appName}}-e2e
- libs
- tools
apps/*
: here go all the application projects - by default, it'll be the app we created and an accompanying e2e tests applibs/*
: where all of the libraries that we create gotools/*
: here, we can put all of the necessary tooling scripts etc that are necessary in our project- and all the root configuration files like angular.json, config files for Jest, ESLint, Prettier, etc
This whole structure is created for us so that we can focus on building the solution right from the beginning.
Migration from an existing Angular project
If you already have an existing Angular app that was built using the Angular CLI, you can still easily migrate to an Nx Workspace. A project that contains only a single Angular app can be migrated automatically with just one command:
ng add @nrwl/workspace
This will install all of the dependencies, required by Nx, and create the folder structure mentioned in the previous section. It will also migrate the app into apps
folder and e2e suite into apps/{{appName}}-e2e
folder. Nx modifies package.json
script, and decorates Angular CLI so you can still use the same commands like ng build
, ng serve
, or npm start
.
It is important to remember that the version of Angular and Nx must match so that this process goes smoothly. For example, if your project is using version 10 of Angular, please make sure to use the latest 10.x.x
version of Nx CLI.
In case you already have multiple projects, you still can migrate with few manual steps described in the Nx docs.
Nx CLI
In the following sections, we will use Nx CLI to simplify performing operations on the monorepo. You can install it globally by running one of the following commands:
npm install nx --global
yarn global add nx
If you don't want to install a global dependency, you can always invoke local nx
via either
npm run nx
or
yarn nx
Create a library
One of the core ideas behind the Nx Workspace monorepo approach is to divide our code into small, manageable libraries. So by using Nx, we will end up creating a library often. Luckily, you can do this by typing one command in the terminal:
nx g @nrwl/angular:lib mylib
This will create a libs/mylib
folder with the library set up so we can build, test, and use it in other libraries or applications right away. To group the libraries you can use the --directory={{subfolderName}}
additional parameter to specify a subfolder under which a library should be created. You don't have to worry about choosing the perfect place for your library from the start, though. You can always move it around later on using @nrwl/workspace:move
schematics, and you can find all the other options for generating a new Angular library in the official docs.
Every library has an index.ts
file at its root, which should be the only access point to a library. Each part of the library that we want to be part of the lib's public API should be exported from this file. Everything else is considered private to the library. This is important for maintaining the correct boundaries between libraries and applications, which makes for more well-structured code.
Affected
One of the greatest things about Nx Workspace is that it understands dependencies within the workspace. This allows for testing and linting only the projects that are affected by a given change. Nx comes with a few built-in commands for that.
npx nx affected:lint
npx nx affected:test
npx nx affected:e2e
npx nx affected:build
Those commands will run lint
, test
, e2e
, and build
targets, but only on projects that are affected, and therefore they will lower the execution time by a lot in most use-cases. The commands below are equivalent to the ones above, but they use more generic syntax, which can be extended to different targets if necessary.
nx affected --target=lint
nx affected --target=test
nx affected --target=e2e
nx affected --target=build
For all of the commands mentioned above, we can parallelize them by using --parallel
flag and --maxParallel={{nr}}
to cap the number of parallel tasks. There are multiple additional useful parameters that the affected
task can take. Please visit the official docs for more details.
Conclusion
Working with a monorepo has a lot of advantages, and Nx Workspace provides us with multiple tools to get the most of that. By using it, we can speed up our development loop by being able to create atomic changes to the repository, and make sure that the whole workspace is compatible with that change. All of this is done with blazing fast tooling that can be scaled to any project size we might have.
In case you have any questions, you can always tweet or DM me @ktrz. I'm always happy to help!