Building Full-Stack React Applications in a Monorepo

Victor Savkin
Nx Devtools
Published in
6 min readAug 9, 2019

--

Nx is a set of extensible dev tools for monorepos that makes building multiple React applications that share code easy.

In this post we will look at how we can build multiple React applications, in combination with their API services, out of reusable libraries.

Often, when writing about Nx, we talk about the benefits of monorepos, dependency graphs, and advanced static analysis. This post, however, will show that we do not need to know any of this to leverage Nx to the greatest extent. The best practices here will enable us to be more effective, immediately.

I also put together a video that goes through the blog post content and more. So if you learn better by watching, check it out:

Creating a New Workspace

Let’s start by creating a new workspace:

yarn create nx-workspace happyorg --preset=react --appName=tickets

yarn create nx-workspace without any flags will run command in the interactive mode.

The result will look like this:

apps/
tickets/
src/
app/
app.spec.tsx
app.tsx
assets/
environments/
index.html
main.tsx
polyfills.ts
.eslintrc
browserlist
jest.config.json
tsconfig.app.json
tsconfig.spec.json
tsconfig.json
tickets-e2e/
src/
.eslintrc
cypress.json
tsconfig.e2e.json
tsconfig.json
libs/
tools/
workspace.json
nx.json
package.json
tsconfig.json

We have two projects here:

  • The tickets application itself
  • A set of e2e tests written with Cypress

We can serve/test/lint/build the application by running:

nx serve ticketsnx test ticketsnx lint ticketsnx build tickets

Nx delegates performing these tasks to other popular tools such as Webpack, Jest, and Cypress but it provides a unified extensible command-line interface for invoking them.

Next, let’s sketch out the tickets app by updating app.tsx:

If we run nx serve tickets, we will see something like this:

Building a Full-Stack Application

Real-world applications don’t live in isolation — they need APIs to talk to. Let’s build one.

Adding an API

What framework should we use?

Nx is an extensible tool and can support any backend framework. For this example, we are going to use express because it is the one most folks are familiar with.

yarn add -D @nrwl/express # add support for express
nx g @nrwl/express:app api --frontendProject=tickets

Which will result in something like this:

apps/
api/
src/
assets/
environments/
main.ts
jest.config.js
tsconfig.app.json
tsconfig.spec.json
tsconfig.json
tickets/
tickets-e2e/
libs/
tools/
workspace.json
nx.json
package.json
tsconfig.json

Let’s update api/src/main.ts to return the array of tickets.

If we run nx serve api and open http://localhost:3333/api/tickets, we will see the json response.

Updating the Frontend to Talk to the API

The last thing that is left is to update our frontend.

With these changes (and by running nx serve api and nx serve tickets), the tickets application is up again. But this time it gets the data from the api.

We have a problem though. We defined Ticket twice: once on the frontend, once on the backend. This duplication will inevitably result in the two interfaces going out of sync, which means that runtime errors will creep in. We need to share this interface.

Sharing Code

Normally sharing code between the backend and the frontend could take a lot of effort, but with Nx, it’s done in just minutes. You do it by creating libraries.

nx g @nrwl/web:lib data

apps/
api/
tickets/
tickets-e2e/
libs/
data/
src/
lib/
data.ts
index.ts
tsconfig.json
tsconfig.spec.json
tools/
workspace.json
nx.json
package.json
tsconfig.json

Now, let’s move the ticket interface into data/src/lib/data.ts.

And import it into both the frontend and the backend.

Let’s pause for a second to see where we are at.

We created two applications: one using React and the other one using Express. We used the same commands to test/serve/build them. And we were able to share code between them.

To see the structure of the workspace, we can run nx dep-graph .

Many to Many

Building a single frontend app that shares code with a single backend app is useful, but it is not how most companies build their systems. Most real-world systems consists of multiple frontends talking to multiple microservices — it’s many to many.

Adding a Second React App

To show how we can do, let’s create a second React application.

nx g app agent

apps/
tickets/
tickets-e2e/
api/
agent/
src/
app/
app.spec.tsx
app.tsx
assets/
environments/
index.html
main.tsx
polyfills.ts
.eslintrc
browserlist
jest.config.json
tsconfig.app.json
tsconfig.spec.json
tsconfig.json
agent-e2e/
src/
.eslintrc
cypress.json
tsconfig.e2e.json
tsconfig.json
libs/
tools/
workspace.json
nx.json
package.json
tsconfig.json

And let’s sketch outagent/src/app/app.tsx:

Extracting Reusable Components

As you can see, tickets and agent are similar looking apps. In particular, the ticket list looks exactly the same in both of them. Let’s extract it into a reusable component. In Nx, you do it by creating a library.

nx g lib ticket-list

apps/
tickets/
tickets-e2e/
api/
agent/
agent-e2e/
libs/
data/
ticket-list/
src/
lib/
ticket-list/
ticket-list.tsx
ticket-list.spec.tsx
index.tx
.eslintrc
jest.config.js
tsconfig.json
tsconfig.spec.json
tools/
workspace.json
nx.json
package.json
tsconfig.json

Now, let’s update libs/ticket-list/src/lib/ticket-list/ticket-list.tsx.

That’s everything we had to do to be able to import the component into our apps.

The nx dep-graph command will show the following:

What we have now is a lot more interesting: we have two React applications built using shared libraries and talking to an API.

Adding a Second API

To finish the many-to-many example, let’s add a second api.

nx g @nrwl/express:app agent-apiapps/
agent-api/
src/
assets/
environments/
main.ts
jest.config.js
tsconfig.app.json
tsconfig.spec.json
tsconfig.json
api/
agent/
agent-e2e/
tickets/
tickets-e2e/
libs/
tools/
workspace.json
nx.json
package.json
tsconfig.json

Let’s update agent-api/src/main.ts:

And agent/src/app/app.tsx

With all of these in place, if we run: nx serve api, nx serve agent-api, and nx serve agent, we will see the following:

Nx is Smart

We have already showed something really cool. We have a repository where we can build multiple React and Node applications and share code between them. And it took us just a few minutes.

But Nx can do a lot more than that. It knows how your apps and libs depend on each other. nx dep-graph visualizes this information:

Since Nx knows the graph, it can be smart about testing and building our projects. Say we make a change to the ticket list component and then runnx affected:dep-graph , we will see the following:

Nx figured out what changed and what can be affected by the change. This means that instead of retesting/rebuilding/relinting everything on every PR, we can only recheck a handful of projects affected by the change.

For a repo of this size, this may not matter as much, but for real life workspaces with 100+ projects it is a necessity.

Source Code

Try it out!

Victor Savkin is a co-founder of Nrwl. We help companies develop like Google, Facebook, and Microsoft since 2016. We provide consulting, engineering and tools.

If you liked this, click the 👏 below so other people will see this here on Medium. Follow @victorsavkin to read more about monorepos, Nx, Angular, and React.

--

--

Nrwlio co-founder, Xoogler, Xangular. Work on dev tools for TS/JS. @NxDevTools and Nx Cloud architect. Calligraphy and philosophy enthusiast. Stoic.