Monorepos : Building with nx: An ExpressJS and NextJS Application

Sriram
5 min readDec 21, 2022
Nx: Next.js | Express

What are Monorepos?

Monorepo: Not a monolith

The textbook definition of a monorepo is “A monorepo is a single repository containing multiple distinct projects, with well-defined relationships.” Which sounds an awful lot like the monolithic architecture, which is really hated in the world of “microservices”. Nothing against monoliths considering Instagram is one of the largest monoliths out there. But, we have seen the cons of using a monolithic; but wait

✋ Monorepo ≠ Monolith

A good monorepo is the opposite of monolithic! Read more about this and other misconceptions in the article on “Misconceptions about Monorepos: Monorepo != Monolith”.

TLDR:

  • Everything at that current commit works together.
  • Changes can be verified across all affected parts of the organization.
  • Easy to split code into composable modules
  • Easier dependency management
  • One toolchain setup
  • Code editors and IDEs are “workspace” aware
  • Consistent developer experience
Source: https://monorepo.tools/#why-a-monorepo

Is Google still using monorepo?

Believe it or not, Google is one of the biggest monorepo users in our industry. I’m not making this up, their code base is huge, as you can probably imagine, and reports state that they have 95% of it inside the same repository. I rest my case 🎯

In all seriousness, I’m just documenting my learning for a project at work. Anyways #LearningInPublic.

Creating a simple api to Fetch Cricket Players 🏏

Let’s start with creating a simple express app and make a REST controller that fetches the data from the our dataset.

Let’s initialize a repository. Initialize a repository using:

npx create-nx-workspace — preset=express

Opens an interactive shell and initializes the repo; repository name = nx-cricket; application name = nx-cricket-api (if you wish to follow to the t)

nx serve nx-cricket-api # to test the app

Directory Structure 😅
So far so good

I’ve downloaded the dataset https://data.world/raghav333/cricket-players-espn and cleaned the dataset and wrote a simple python script to convert it into a JSON to make it easier export it as a typescript array. Note: the entire Code/Dataset is available on GitHub.

export interface Cricketer {
Id: string
Name: string;
Country: string;
'Full name': string;
Age: string;
'Major teams': string;
'Batting style': string;
'Bowling style': string;
Other: string;
}

export const cricketers: Cricketer[] = [
{
"Id": "0",
"Name": "Henry Arkell",
"Country": "England",
"Full name": "Henry John Denham Arkell",
"Age": "84",
"Major teams": "Northamptonshire",
"Batting style": "Right-hand bat",
"Bowling style": "",
"Other": ""
}, ...
]

Define a Simple Controller in nx-cricket-api>src>main.ts

Lets just define two simple controllers to retrieve all the cricketers and Query endpoint to return cricketer by name.

// Returns all the cricketers 
app.get('/cricketers', (_, res) => {
res.send({cricketers});
});

// Returns cricketer: Lazy Search by Name
app.get('/search', (req, res) => {
const q = ((req.query.q as string) ?? '').toLocaleLowerCase();
res.send(cricketers.filter( ({ Name }) =>
Name.toLocaleLowerCase().includes(q)
));
});
localhost:3333/cricketers
localhost:3333/search?q=Barb

Let’s create a simple frontend with Next.js

First we have to install the nrwl/next if it has not already been installed. Then we create a simple next app, a next app boilerplate is generated. We add a simple application that renders the list of cricket players

# Installing nrwl/next

# yarn
yarn add --dev @nrwl/next

# npm
npm install --save-dev @nrwl/next

# create nx app
nx g @nrwl/next:app
name = nx-cricket-search
style = css
Api, Frontend and Test folders generated

Removed all the boilerplate and added this basic frontend code

import { useEffect, useState, useCallback } from 'react';
import React from 'react';

import { Cricketer } from '@nx-cricket/shared-types';

export function Index() {
const [search, setSearch] = useState('');
const [cricketer, setCricketer] = useState<Cricketer[]>([]);

useEffect(() => {
fetch(`http://localhost:3333/search?q=${escape(search)}`)
.then((resp) => resp.json())
.then((data) => setCricketer(data));
}, [search]);

const onSetSearch = useCallback(
(evt: React.ChangeEvent<HTMLInputElement>) => {
setSearch(evt.target.value);
},
[]
);

return (
<div>
<input
style={{ padding: '10px', margin: '20px' }}
value={search}
placeholder="Enter Cricketer Name"
onChange={onSetSearch}
/>
<ul>
{cricketer.map(({ Id, Name, Country, Age }) => (
<li key={Id}>
{Name}, {Country}, {Age}
</li>
))}
</ul>
</div>
);
}

export default Index;
Lazy Search :)

You can also add server-side rendering. But for this tutorial I’ve decided to keep it simple.

Shared types

One of the best features of monorepos is the ability to use shared types, if you see the frontend code, we have strongly typed the cricketer array, this was because we were able to add the type ~ interface in shared-types folder from the Cricketer.ts file. This is one of the most useful features of the monorepo structure.

# Creating a shared Library
nx g @nrwl/node shared-types

# In libs>shared-types>src>index.ts add | already found in our Cricketer.tsc
export interface Cricketer {
Id: string
Name: string;
Country: string;
'Full name': string;
Age: string;
'Major teams': string;
'Batting style': string;
'Bowling style': string;
Other: string;
}

# Cleanup cricketer.ts
import type { Cricketer } from "@nx-cricket/shared-types"

export const cricketers: Cricketer[] = [
{
"Id": "0",
"Name": "Henry Arkell",
"Country": "England",
"Full name": "Henry John Denham Arkell",
"Age": "84",
"Major teams": "Northamptonshire",
"Batting style": "Right-hand bat",
"Bowling style": "",
"Other": ""
},...
]

# type { Cricketer } from "@nx-cricket/shared-types" can be accessed from any project
Dependency Graph for our Project: generated with nx graph

--

--

Sriram

Computer Science Undergraduate. Passionate about Machine learning, AI , Big Data. Also working on blockchain / Dapps.