Nx and Node Microservices

Jonathan Cammisuli
Nx Devtools
Published in
6 min readFeb 5, 2021

--

Nx + Node
https://nx.app

In this post, we’ll go over how we can use Nx to create microservices using Node and Docker.

We’ll be creating a simple product that will have two applications (a view application, and an API application).

And we will have a Docker container running on http://localhost:8080 that will have two services communicating with each other within the container.

Prerequisites

Since we’re going to be creating images with Docker, it will need to be installed first. You can follow Docker’s install guide at https://docs.docker.com/desktop

Getting Started

Let’s start by creating a new Nx workspace, run either one of the following:

npx create-nx-workspace --preset=nest

or

yarn create nx-workspace --preset=nest

Then set the following options:

Workspace name (e.g., org name)     my-org
Application name api

Next, we’ll add another application. Change directories into the newly created workspace (cd my-org) and run the following command:

npm run nx generate application html

or

yarn nx generate application html

After the html project is created, we’ll add a Node library to share some interfaces between the html and api projects.

Run the following commands to install @nrwl/node and create a library project:

npm install -D @nrwl/node
npm run nx generate @nrwl/node:library todos

or

yarn add -D @nrwl/node
yarn nx generate @nrwl/node:library todos

For future examples, I’ll omit npm or yarn, to keep the commands short. You can continue using whichever tool you’re comfortable with.

After everything is set up, we should have the following project structure:

.
├── apps/
│ ├── api/
│ │ ├── src
│ │ │ ├── // source files
│ │ ├── jest.config.js
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ └── html/
│ │ ├── src
│ │ │ ├── // source files
│ │ ├── jest.config.js
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
├── libs/
│ └── todos/
├── tools/
├── jest.config.js
├── jest.preset.js
├── nx.json
├── package.json
├── tsconfig.base.json
└── workspace.json

Now that we have Nx workspace set up we can begin editing some code

Let’s start with editing libs/todos/src/lib/todos.ts and just export an interface:

Then edit both main files for app and html (apps/api/src/main.ts and apps/html/src/main.ts) and remove the globalPrefix variables. We won’t be needing this because we want this application to just run directly on the host without having to go into deep URLs.

API service

Now that some setup is done, we can edit the apps/api/src/app/app.service.ts file to send some todos.

Notice that we can import directly from the todos library without having to go deep into relative paths. This is one of the abilities that is provided by a Nx workspace automatically when creating libraries. 👍

Run the application with the following command:

nx serve api

And verify that data is returned when we go to the endpoint via the browser at http://localhost:3333. We should see some JSON in the browser, with some expected todos. 🎉

HTML service

We should now have our API service working as expected. Now we need to change the htmlproject to call the api and render a UI.

We’ll need to install some dependencies:

yarn add axios hbs

Axios will be used to do some HTTP calls from Node easily.
Hbs is a template engine used to convert Handlebars templates to plain html.

Now let’s set up our html project to render some Handlebars templates.

Create the following file under the apps/html/src/assets/views/index.hbs path.

Then let’s modify apps/html/src/main.ts to enable server side templates:

Something that’s important to note is that we also updated the port to start on 3334. We need this so that we can start both the api application and html application together

Next up, modify apps/html/src/app/app.controller.ts so that the getData retrieves data from the api application. We’ll also need to add an index renderer.

This application imports the same Todos interface from @my-org/todos. This will keep the html and the api application in sync.

Also to note: The Nest framework provides a microservices package that would be better suited for production. You can read more about that on the Nest documentation site.

Now the html application is ready to get data from the running api application.

Running multiple applications

To test locally, we will need to make sure that both projects are running. Thankfully, Nx provides a mechanism to run multiple projects together.

If you still have your api application running, stop that process.

In the terminal, run the following command:

nx run-many --target=serve --projects=api,html --parallel

This is the equivalent to running nx serve <project> in multiple terminals.

If you open the http://localhost:3334 link in your browser we should see a UI that retrieved data from another service

Building Docker images

Let’s build our applications to get them ready to be put into Docker images.

When creating Docker images for Node applications, we need to make sure that there’s a package.json available. We need the package.json to install application dependencies in the image.

Nx has mechanisms to automatically create a package.json based on dependencies an application is using. To enable this, we need to modify the workspace.json file and enable package.json generation.

Add the generatePackageJson property to each of the build targets:

Now we can run the following command to build the applications:

nx run-many --target=build --projects=api,html --parallel

Now we should have a couple folders in the dist/ directory. We can also see the generated package.json files as well.

If we compare the two package.json files we can see that the html project contains more dependencies. This is because we’re using these explicitly in the html application.

Next we’re going to create a couple Dockerfiles.

Create the following in the apps/api/Dockerfile path:

FROM node:lts-alpine
WORKDIR /app
COPY ./dist/apps/api .
ENV PORT=3333
EXPOSE ${PORT}
RUN npm install --production
# dependencies that nestjs needs
RUN npm install reflect-metadata tslib rxjs @nestjs/platform-express
CMD node ./main.js

And one in the apps/html/Dockerfile:

FROM node:lts-alpine
WORKDIR /app
COPY ./dist/apps/html .
ENV PORT=3334
EXPOSE ${PORT}
RUN npm install --production
RUN npm install reflect-metadata tslib rxjs hbs
CMD node ./main.js

There’s an additional RUN npm install command in each of the Docker files. These are the dependencies that NestJs needs that aren’t explicitly used in our applications.

We can confirm that the docker images build by running the following commands in the root of the workspace:

docker build -f ./apps/api/Dockerfile . -t api
docker build -f ./apps/html/Dockerfile . -t html

Deploy command

Having to remember all these commands can be confusing. Thankfully, Nx has another mechanism where we can combine multiple commands together. This is provided by the @nrwl/workspace:run-command executor.

Let’s modify the workspace.json file and add a new target: deploy.

Now if we want to always build fresh images, we can run nx deploy api or nx deploy htmland Nx will run the build command then call docker to build the image.

Composing

There are a couple more things we need to do to make sure that our apps are ready to be run in a container with each other.

Create a docker-compose.yml file in the root of the workspace with the following:

version: '3.8'
services:
html:
image: html
environment:
- apiPath=http://api:3333
ports:
- '8080:3334'
api:
image: api
ports:
- '8081:3333'

In the case of Docker Compose, images can communicate with each other by their names in services. So for the html application to communicate with api, we have to make sure that the URL is set up correctly. Calling localhost:3333 from the html container will not work.

So we included the environment key with an apiPath variable that would map to the apicontainer’s address.

Now we need to modify the html application to know about this new environment variable.

Edit the apps/html/src/app/app.controller.ts file with the following:

Since we modified the html application, let’s rebuild everything for it with nx deploy html.

We can also run nx affected --target=deploy and use Nx’s affected algorithm to automatically build only projects that were changed

When everything is built and deployed, run the following command:

docker-compose up

Once everything is finished setting up, open localhost:8080 in your browser. You should now see the html application running with our todos! 🎉

Summary

In the end, this is what we did:

  • Created a new workspace
  • Created a library, and applications
  • Set up Server side rendering for one application
  • Created Dockerfiles
  • Added new targets in our workspace.json
  • Built our applications and deployed Docker images
  • Ran our images in a container with docker-compose

docker-compose isn’t the only tool we could have used to orchestrate containers. As a next step, diving into Kubernetes could be fun and interesting!

Hopefully this guide will help you get started with how you can implement Docker with your own Nx workspace.

You can see the final code at github.com/nrwl/node-microservices.

--

--