Getting Started with Nx: The Nrwl Extensions for Angular

In this article we will learn how to create an Angular Nx Workspace and scaffold it out with multiple apps and libs in a mono-repo style approach.

What is Nx

Nx is an open source toolkit for enterprise applications that is created and maintained by the team at Nrwl. It's based on their experience working at Google and helping the Fortune 500 build ambitious Angular applications.

Nx is an extension for the Angular CLI and is made up of the following:

  • A set of schematics for project and code generation (@nrwl/schematics)
  • A helper library for handling common tasks like data persistence with NgRx and testing (@nrwl/nx)
  • A set of binaries that can be run from the terminal to help with formatting, linting and code base analysis

Learn more about Nx by visiting nrwl.io/nx

An Nx Workspace is an Angular CLI project configured to make working with multiple Angular applications and libs within a single repo more reliable. It also helps facilitate the architecture approach of having a thin layer at the app level (an app NgModule that handles configuring things) and the rest in libs that can either be app specific like feature modules or reusable across apps like component libraries or utilities and services.

Getting Up and Running with an Nx Workspace

Let's walk through setting up the initial architecture structure for a workspace. Say we are creating a platform for a podcast show. We plan to have a user facing app that provides info about the podcast as well as a list of episodes, and we plan to have an admin app for managing the podcast show information. We've also identified that there will be components and logic that will be common between both of these apps.

Step 1: Create an Nx Workspace

Since Nx is an extension for the Angular CLI, the creation of an Nx Workspace involves either creating a new CLI project or adding Nx to an existing one. The current major version of Nx (6.x) aligns with the current version of the Angular CLI (6.x). So creating an Nx Workspace involves using the Angular CLI.

There are two ways to go about creating a new Nx Workspace, using a binary that is provided by @nrwl/schematics or using the Angular CLI ng new command. The binary is the current recommended way as the ng new command requires some additional steps. But we'll walk through both here.

Generate a New Project: The create-nx-workspace Command

The @nrwl/schematics library contains a binary script that supports the command create-nx-workspace. We can make use of that by first installing the @nrwl/schematics globally. Now, since an Nx Workspace is an Angular CLI workspace under the hood, we will most likely want to install the Angular CLI globally as well. We can do both with the following command:

With these installed, we can make use of the create-nx-workspace command to create a new Nx Workspace:

This will create a new Nx Workspace folder named podcast-platform in the current working directory (place where you ran the command) with no apps or libs to start...just the structure in place. We will see how we can add apps in the next step.

Nx Workspace Initial Directory Structure

Generate a New Project: The ng new Command

The create-nx-workspace handles running the ng new command along with the options it needs to leverage the Nx schematics for creating a new workspace. If we wanted to, we could do that process manually. But, as of the writing of this post, there is a minor issue that requires a workaround with Nx when creating a new workspace. We need to install the NgRx schematics globally before running the ng new command. This can be done with the following command:

With that installed, we can use the Angular CLI new command and to create our new Nx Workspace:

The --collection option is used to tell the Angular CLI that we want to use the Nx schematics collection and its new command. The --directory option is used to tell the Angular CLI what directory to put the workspace files in.

So we can do this process manually, but if we use the create-nx-workspace command then all of the above is handled for us!

... or Add Nx to an Existing Project

The Angular CLI (version 6.x and above) has an add command that can be used to add stuff to an existing Angular CLI workspace. If we already have a workspace set up (and that only has 1 app project in it), we can use the add command to add the @nrwl/schematics to it and it will handle installing the dependencies needed and moving files around to match the structure of an Nx Workspace:

Step 2: Generate Angular Applications

The Nx schematics collection has a schematic named app for adding a new Angular application project to the workspace. It has several options (which we can see if we run ng generate app --help) that can be used when creating a new app. Let's use that to create the user facing app and the admin app in our Nx Workspace, and we'll make use of the routing flag that will add the initial bits for setting up routing (the router module, a default route, and a router outlet).

We can run the following generate command to add the user facing app (which we'll name website):

And we can run the following generate command to add the admin app (which we'll call admin):

Running these will result in four new projects created within the angular.json file (the two apps and an end to end test suite, or e2e, for each of them).

And it will put the source code for those projects in the apps directory in the workspace.

Apps In Nx Workspace

That source code will be similar to the app code the Angular CLI sets up for a new project with the addition of that routing code we asked it to add for us with the --routing flag.

Step 3: Generate Libs

Libs in an Nx Workspace are designed to be the home for all of your application code outside of its setup and configuration. So things like feature modules, routed modules, shared modules, reusable component modules, etc. And these can sit directly in the libs directory or they can be nested to help organization and reasoning.

Let's create some libs for the code that will power our podcast applications. The Nx schematics collection has a schematic named lib that can be used to create new libs. Just like the app schematic, the lib schematic has several options which we can see if we run ng generate lib --help. We will make use of some of the routing options and the directory option when we create our libs.

Feature Libs (Routed)

We can begin with the bits for the user facing app (website). Say we want to have a home screen and an episode details screen and we want to route to those and make the episode details screen be lazy loaded. And these screens will be specific to the website app.

We can add a lib for the home screen like so:

With this command we are doing the following:

  • Naming the lib home-screen which will create an NgModule by that name
  • Using the directory flag to create a nested directory in the libs directory with the name website to help us organize libs that are specific to the website app
  • Using the routing flag to indicate that this lib will be a routed module
  • Using the parent-module flag in conjunction with the routing flag to tell it which existing NgModule to add this new lib as a route entry to

And then we can add the lib for the episode details like so:

With this command we are doing the same as the previous lib we added with the addition of the following:

  • Specifying that we want this routed lib to be lazy loaded with the lazy flag, which will set up the route entry with the loadChildren property and string value to make it lazy loaded

The lib schematic will also take care of some plumbing in the workspace config files to make it so we can import from these libs using an npm scope shorthand (like @podcast-platform/website/home-screen) as well as "barrel files" for controlling what we want to export from these libs as usable by other libs and apps.

For the admin app we can do a similar approach to creating the new libs. Say we want a dashboard screen, a lazy loaded screen for episodes and one for guests. We can run the following:

Shared and Reusable Libs

With our apps and feature libs in place, we can focus on scaffolding out the libs that we intend to share across apps. Say we wanted to create some presentational components to display our podcast data in a common way. We could create a common-ui lib for that stuff.

This will create a CommonUiModule that we can use that will declare those components that we can use within the feature modules for the apps. One set of code to maintain between apps, and ready for reuse if we add more apps in the future.

Now, we could go and manually add that component code that we need to this lib by creating the files in the libs/common-ui/src/lib dir, but the Angular CLI provides schematics for generating that code. And those schematics support an option flag named project that can be used to tell it which project to generate it in. Apps are created as project entries, and so are libs. So we can use the --project flag to add a new component to the common-ui lib:

Then from there we can add the CommonUiModule to the imports array in the NgModule metadata for the other libs where we intended on using it:

With that --project flag, we can use the Angular CLI schematics to add components, directives, services and pipes to any of the libs that we create.

And with the barrel files (the index.ts files in each lib) we can control what bits we intend to be able to use outside of those libs (make public). So if we wanted to make a service that formats the podcast data in a certain way for that episode summary component but we don't intend for that service to be re-used outside of that component we can simply leave it out of the exports in the index.ts file. Nx has custom linting rules that will help enforce code boundaries, so attempting to deep import that service will result in an error in both our IDE/editor as well as the build.

Where to Go From Here

We can use the Angular CLI schematics to scaffold out the components, directives, sub modules and more throughout our libs and we can write specs within those libs for that code (and can run lib specific tests by using the ng test <project-name> command). If we have multiple teams, each team can own a lib (or libs) and have a manageable way to work that code base.

We can serve/build individual apps by using the project name along with those commands (ng serve admin or ng serve website). And we can configure the port to use for each app in the angular.json file so that we can serve up each app concurrently (via the projects/<project-name>/architect/serve/options/port property in the json object).

Beyond the basic application scaffolding and development workflow, an Nx workspace has a set structure and pattern to it, and because of this Nx is able to analyze the code base and identify if the architecture and usage is as expected and understand the layout and dependencies of it. In addition to the custom linting rules mentioned earlier, Nx has commands to help auto-format your code (using Prettier), display a visual dependency graph of your Angular modules, and even provide the opportunity to tag library types in the nx.json file so we can specify intent and get notified (via linting rules set up in tsconfig.json) when we attempt to use lib code in an unintended way.

To learn more about what Nx provides visit the documentation at https://nrwl.io/nx/guide-nx-workspace.

If your company or team is experiencing challenges with architecting and engineering Angular applications at scale, the team at Nrwl can help! Visit https://nrwl.io to learn more and to get in touch.

About the Author

Justin Schwartzenberger
Justin Schwartzenberger is a Google Developer Expert for Angular and Web Technologies, the host of the weekly AngularAir live video broadcast, author of online Angular training courses, a frequent conference speaker, and an open source contributor. You can follow Justin on Twitter @schwarty and find out more about him at schwarty.com.