Developing Next.js microservices with Nx
Tags: nextjs, webdev
Published: 2023-12-28 19:02:16

**Written by [Alec Brunelle](https://blog.logrocket.com/author/alecbrunelle/)✏️**

As organizations grow, so does the quantity and complexity of the software required to sustain the trajectory of the business. Microservices are a common pattern that developers use to split software services into smaller pieces, making them easier to maintain and improving sustainability. 

When multiple web applications need to be built for an organization, they can be separated into individual pieces and loosely coupled. Teams that adopt this pattern become autonomous and enjoy independent deployments, domain logic, and a host of other benefits. 

In this article, we’ll discuss some considerations and tradeoffs associated with using Nx to develop microservices. Nx is a development framework for building applications inside of monorepos. Monorepos contain multiple apps inside of a single Git repository. Nx handles many monorepo use cases like build systems, inbuilt tools, and smart caching. We’ll demonstrate how to use Nx to create multiple Next.js apps within a monorepo.

## Benefits and tradeoffs of monorepo structures

Monorepos enable organizations to share code, such as components and utility libraries, across apps and teams. This structure can be particularly helpful for companies that have reached a critical growth stage where there are many web applications to maintain and build features for and multiple API servers across the organization that must be cared for. 

At this inflection point, the number of Git repositories increase, processes start to differ, and it becomes increasingly difficult to keep track of all the code being used across the organization. [With a monorepo, one Git repository holds all the code you need](https://blog.logrocket.com/building-nx-monorepos-remix/#benefits-using-monorepo-structure), split up the way your organization wants. For example, if there is a common React authentication component, you could store this in a library package that’s used by multiple Next.js apps. 

Another benefit of monorepo structures is that changes between layers of the stack can be propagated in a single pull request. For example, a developer could create a single pull request that updates an API server route, change the shared TypeScript type package, and integrate it with a connected frontend component. 

As with anything, there are some tradeoffs associated with monorepos, such as longer build times and the challenge of keeping code consistent across the repository. That’s where solutions like Nx, Yarn and npm workspaces, and Bazel come into play. These [tools are designed to make monorepos easy to use](https://monorepo.tools/#why-a-monorepo) by providing a variety of solutions like task orchestration, caching, generators, and dependency graph analysis.

## Creating monorepos with Nx

[Nx](https://nx.dev/) is a robust monorepo solution that offers a first-class experience for creating Next.js microservices. It offers templates and commands, allowing developers to quickly start using monorepos. 

The Nx CLI enables consistent generation of new apps and packages and offers first-class support for JavaScript and TypeScript. But, the advanced features make Nx really shine. For example, Nx lets developers declare task dependencies, such as having an API run a build task before a frontend build task. 

Nx also supports running tasks only on “affected” code, which helps shorten build task times when a change spans multiple apps or packages. Nx supports many common monorepo functionalities and is feature-rich compared to other competing tools ([check out the breakdown here](https://monorepo.tools/#monorepo-tools)). For additional information about Nx, [refer to the official docs](https://nx.dev/core-features).

## Choosing the Nx path

Nx is available in a few versions. [Integrated Repos](https://nx.dev/getting-started/tutorials/integrated-repo-tutorial) is an opinionated fully-fledged version that hooks into existing build tools. It offers a single `package.json` file for the entire monorepo for ease of package management. It is more difficult to add to an existing project as it hooks into code bundling, tools like Jest and webpack need to be wrapped in special Nx packages to make them work. 

[Package-Based](https://nx.dev/getting-started/tutorials/package-based-repo-tutorial) Nx is similar to how workspaces work in Yarn and npm. Each package has its own set of dependencies. 

For this tutorial, we’ll stick with the Integrated Repos version of Nx.

## Building Next.js applications in Nx

Many growing companies use Next.js to build their web apps because it provides important inbuilt features such as data fetching, image optimizations, and dynamic HTML streaming. Nx provides excellent support for Next.js, enabling developers to easily generate Next.js apps in a consistent manner. 

The `@nx/next` npm package offers specific tools and integrations, including Nx-specific code generators and build tools. For example, there’s a Next.js plugin called [`withNx` that helps Next.js understand the monorepo libs structure and other Nx-specific features.](https://nx.dev/recipes/next/next-config-setup) 

Nx tools provide developers with a documented approach to creating multiple Next.js apps within a monorepo. This enables the sharing of component libraries and utility libraries, while also supporting the latest Next.js features.

## Creating multiple Next.js apps with the same authentication

For our tutorial, let’s consider a hypothetical example. Organization Y is growing and now has two web applications it needs to develop: a list and a forum. Both apps will need authentication. 

The frontend team decides to build two Next.js applications. They’ve been told that authentication should have a consistent look and feel across both websites. A separate backend development team creates one authentication API for the entire organization to use. 

The developers agree that it feels wasteful to create everything twice for two websites that are meant to have identical content and behavior. So, they opt to build the two Next.js apps inside one Nx monorepo. 

The project they create has an Authentication React component inside a `ui-lib` package and a `login-api` service serving the API. To make the frontend truly type safe, the team creates a `login-api-types` package that holds the API types. 

Here’s the project’s dependency graph: ![Dependency Graph Generated Nx](https://blog.logrocket.com/wp-content/uploads/2023/12/dependency-graph-generated-nx.png) Here’s how the final project looks: ![Final Nx Monorepo Project](https://blog.logrocket.com/wp-content/uploads/2023/12/final-nx-monorepo-project.png) Let’s walk through building this sample Nx monorepo project.

### Setting up the project

For this project, we’ll create two Next.js applications: `list` and `forum`. Then we’ll use Node.js and Express to create the `login-api`. If an user enters `admin` for the username and `password` for the password, they will be redirected to the homepage. 

**N.B.,** to keep things simple, choose the `derived` option for file naming and use CSS for styling whenever possible 

To start, ensure you have Node.js installed:

```bash
node --version
// This should output &gt;= 18.x.x 
```

Now, go to your home directory where you store GitHub repositories; mine is `~/Github`:

```bash
cd ~/Github
```

Create a monorepo for the `yorg` using `npx` and the `create-nx-workspace` npm script, like so:

```bash
npx create-nx-workspace@latest yorg --preset=ts
```

The above command creates a folder with all necessary boilerplate for an integrated workspace using TypeScript. Next, `cd` into the directory:

```bash
cd yorg
```

Install the Next.js Nx plugin; this will enable you to generate Next.js apps within the monorepo in a consistent fashion and also includes Next.js plugins that we’ll use later in this tutorial:

```bash
npm i -D @nx/next@17.1.1
```

### Creating the Next.js applications

Now it’s time to create the `list` and `forum` Next.js applications. To start, install the Nx CLI globally:

```bash
npm i -g nx@17.1.1
```

Use the Nx CLI to create two Next.js apps:

```bash
mkdir apps
nx generate @nx/next:application --name=list --directory=apps
nx generate @nx/next:application --name=forum --directory=apps
```

Next, run a command to generate the login page in both the list and the forum. This is a React component that renders when a user navigates to `<rootURL>/login`:

```bash
nx g @nx/next:page --name=login --project=list
nx g @nx/next:page --name=login --project=forum
```

Before moving on, serve them with the below commands and visit `http://localhost:4300` to see if the apps work:

```bash
nx serve list
nx serve forum --port 4300
```

Here’s the `list` application; the `forum` app should look the same: [caption id="attachment_183839" align="aligncenter" width="895"]![Nx Next.js App Homepage](https://blog.logrocket.com/wp-content/uploads/2023/12/nx-next-js-list-app-homepage.png)

### Creating the shared authentication component

Next, we’ll create the shared authentication component. To start, create a Nx library, named `ui`:

```bash
mkdir lib
nx g @nx/next:library ui --directory=lib
```

Then, create a Next.js React component, named `auth-component`:

```bash
nx g @nx/next:component auth-component --project=lib-ui
```

Now, add basic input HTML elements and a login button inside the `lib/ui/src/lib/auth-component/auth-component.tsx` file:

```typescript
import styles from './auth-component.module.css';
import { useState } from 'react';

export function AuthComponent(props: AuthComponentProps) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = () =&gt; {
    // Handle login logic here
  };

  return (
    &lt;div className={styles['container']}&gt;
      &lt;h1&gt;Login to Y Organization&lt;/h1&gt;
      &lt;input
        type="text"
        value={username}
        onChange={(e) =&gt; setUsername(e.target.value)}
        placeholder="Username"
        id="username"
        className={styles['input']}
      /&gt;
      &lt;input
        type="password"
        value={password}
        onChange={(e) =&gt; setPassword(e.target.value)}
        placeholder="Password"
        id="password"
        className={styles['input']}
      /&gt;
      &lt;button onClick={handleLogin} id="login-button" className={styles['button']}&gt;Login&lt;/button&gt;
    &lt;/div&gt;
  );
}
export default AuthComponent;
```

Next, add some basic CSS inside the `lib/ui/src/lib/auth-component/auth-component.module.css` file:

```css
.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f4f4f4;
  color: #333;
}

.input {
  margin: 10px 0;
  padding: 10px;
  border: none;
  border-radius: 5px;
  background-color: #fff;
  color: #333;
}

.button {
  margin: 10px 0;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  background-color: #123f6e;
  color: #fff;
  cursor: pointer;
}

.button:hover {
  background-color: #0056b3;
}
```

To use the `auth-component` inside your list and forum apps, open the `lib/ui/src/index.ts` file and a command to export from `./lib/auth-component/auth-component`, like so:

```typescript
'use client';

export * from './lib/lib-ui';
export * from './lib/auth-component/auth-component';
```

Now, add the `auth-component` to the login pages in the `apps/list/app/login/page.tsx` and `apps/forum/app/login/page.tsx` files:

```typescript
import { AuthComponent } from '@yorg/lib/ui';
import styles from './page.module.css';

/* eslint-disable-next-line */
export interface LoginProps {}

export function Login(props: LoginProps) {
  return (
    &lt;div className={styles['container']}&gt;
      &lt;h1&gt;Welcome to Login!&lt;/h1&gt;
      &lt;AuthComponent /&gt;
    &lt;/div&gt;
  );
}

export default Login;
```

Serve up the apps to make sure they render properly:

```bash
nx serve list
nx serve forum
```

### Setting up the Node.js API server

The next step is to set up the Node.js API server. Start by installing the [Nx Express](https://nx.dev/nx-api/express) plugin:

```bash
npm i --save-dev @nx/express
```

Now, create a new Express app:

```bash
nx g @nx/express:app login-api --directory=apps/login-api
```

Next, create a basic `login` route that returns a `200` response when a user enters `admin` and `password` as credentials:

```typescript
// LoginRequestBody doesn't exist yet!
app.post(
  '/api/login',
  (req: Request&lt;unknown, unknown, LoginRequestBody&gt;, res) =&gt; {
    // if the username is 'admin' and the password is 'password' return a 200 status code
    if (req.body.username === 'admin' &amp;&amp; req.body.password === 'password') {
      res.status(200).send();
    }
    // otherwise return a 401 status code
    res.status(401).send();
  }
);
```

Create a TypeScript library package to house the types for the request body input:

```bash
nx g @nx/js:lib login-api-types --directory=lib/login-api-types
```

Now, insert the types into the `libs/login-api-types/src/lib/login-api-types.ts` file:

```typescript
>export interface LoginRequestBody {
  username: string;
  password: string;
}
```

Use the types inside the `/login` API route inside the `apps/login-api/src/main.ts` file:

```typescript
import { LoginRequestBody } from '@yorg/login-api-types';

app.post(
  '/api/login',
  (req: Request&lt;unknown, unknown, LoginRequestBody&gt;, res) =&gt; {
    ...
  }
);
```

### Calling the login API route from the Next.js apps

To call the login API route, write a login handler function inside the `auth-component` in the `lib/ui/src/lib/auth-component/auth-component.tsx` file:

```typescript
import { LoginRequestBody } from '@yorg/login-api-types';

export function AuthComponent(props: AuthComponentProps) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = async () =&gt; {
    const requestBody: LoginRequestBody = {
      username,
      password,
    };
    // call the login api using the inbuilt browser fetch library at http://localhost:3333/api/login with the username and password as the body
    // if the response is 200, then redirect to the home page
    // if the response is 401, then show an error message
    const response = await fetch('http://localhost:3333/api/login', {
      method: 'POST',
      body: JSON.stringify(requestBody),
      headers: {
        'Content-Type': 'application/json',
      },
    })
    if (response.status === 200) {
      window.location.href = '/';
    } else {
      alert('Invalid username or password');
    }
  };
  ...
};
```

### Checking the work

To verify that everything works, serve up the list and forum apps, visit the login pages and provide `admin` and `password` as the login credentials. If everything is working properly, you’ll be redirected to the homepage.

## Conclusion

As an organization grows so does the complexity of managing its increasingly diverse codebases. Monorepos, coupled with tools like Nx, offer an effective solution to centralizing code while preserving team autonomy. 

Nx integrates with Next.js to provide a streamlined approach for building scalable and consistent applications within a monorepo. Despite tradeoffs, such as longer build times, Nx is invaluable for managing complexity in modern software development. 

In this article, we looked at a practical example that demonstrates the benefits of using Nx for creating shared components and APIs, showcasing its potential for organizations seeking efficient and maintainable development practices. Embracing Nx in the evolving landscape of software development can pave the way for sustainable and scalable solutions.

---

##[LogRocket](https://lp.logrocket.com/blg/nextjs-signup): Full visibility into production Next.js apps

Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket](https://lp.logrocket.com/blg/nextjs-signup).

[![LogRocket Signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://lp.logrocket.com/blg/nextjs-signup)

[LogRocket](https://lp.logrocket.com/blg/nextjs-signup) is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your Next.js apps — [start monitoring for free](https://lp.logrocket.com/blg/nextjs-signup).