How to setup a Next.js and NestJS Monorepo (For Dummies) 🤓

Shaquille Ndunda - Sep 18 - - Dev Community

As a developer, I remember the first time I came across the term monorepo. Now as much as the term in itself is as self explanatory as it possibly could be, I still hadn’t heard of it before and so it prompted me to do a little bit more digging into what exactly a monorepo is. I already knew mono is ‘single’ or ‘one’, and repo is short for repository (git) - that place where we store and manage code. So I guess that’s what a monorepo is? A single repository? Yes!

Perfetto Yes GIF

But there’s still a tad bit more to it. See, it's a single repo that contains mutliple projects or apps. Simple as that. Think about this way — imagine your phone. You have different apps ranging from social, to banking, games, productivity, and so forth, right? And they all run on the same operating system whether iOS or Android, which (the OS) provides common services to them like notifications, internet access, and system updates even if all these apps all do different things.

So what exactly is a monorepo?

Well, it harbours different projects or apps but all within the same system, and they all share common tools, libraries and infrastructure. This makes it easier to update things in one place and ensure that all the projects work well together, just like how your phone updates all its apps through the same system. Interestingly enough, quite a few big names in tech use monorepos like Google, Microsoft, Uber, Airbnb, etc. Anyway, as I continued digging more, I came across the term monolithic application as well. This begged me to ask the question, what’s the difference between a monorepo and a monolithic application? Well, that’s a story for another day when we get into microservices as well. Main focus today is on monorepos, and how to set one up — for dummies! 😁

How to set up one — a monorepo

At this point, I’m assuming as much as you are a dummy like I was when first learning about monorepos, you still have some prerequisite knowledge on Git, package managers (npm, yarn, pnpm, etc.), CLI commands, and frameworks — Next.js and NestJS in this case. But just incase you don’t, you’ll need to have the following things checked off in your development environment setup list:

  1. Node installed - Download Node.js®
  2. Install and set up Git - Setting up Git
  3. Nest CLI installed globally:
npm i -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode

Setting up NestJS

Alright, back to where we were! We’ll be setting up our monorepo from scratch, so we won’t exactly use tools like Nx, Turborepo or Lerna—we’re still dummies, one step at a time before we get to any kind of complexities surrounding the topic. Our monorepo will be called another-juan, get it? Lol. So let’s start by creating the directory/folder:

mkdir another-juan && cd another-juan 
Enter fullscreen mode Exit fullscreen mode

Then run the npm init command inside the another-juan folder to create a package.json file. After that, we’re going to create an apps directory inside the root of our repository (another-juan):

mkdir apps && cd apps
Enter fullscreen mode Exit fullscreen mode

Inside the apps directory, we’ll create a new NestJS app—let’s call it backend. In addition, we won’t initialize git as we’ll do that in the root level of our repository. Lastly, we’re going to use npm as our package manager—simply due to the fact that it's one of the most, if not most popular package managers. The following command will help us with this:

nest new backend --strict --skip-git --package-manager=npm
Enter fullscreen mode Exit fullscreen mode

NestJS Project Installation Success

The --strict flag adds TypeScript strict mode options to the project that will help us catch more potential errors early. Once the project has successfully been created, navigate to the src/ directory and locate the main.ts file.

NestJS File Tree

It should look like this once you open it:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
Enter fullscreen mode Exit fullscreen mode

By default, our backend app is hosted on port 3000. We need to modify this and have it on a different port as 3000 is where our Next.js app will be hosted. We can change it to 3001 to avoid conflict of our local servers. Just to add, we can also use an environment variable for the port and set 3001 as a fallback. This is because during deployment, your hosting provider will likely assign a port for you automatically.

Our main.ts file should now look like this:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT || 3001);
}
bootstrap();
Enter fullscreen mode Exit fullscreen mode

To start the project, simply run the following command in your terminal:

npm run start   
Enter fullscreen mode Exit fullscreen mode

In your browser, head over to the url bar and go to http://localhost:3001. If everything’s gone right up to this point, you should see a “Hello World!” message displayed as seen below:

Hello World in Browser

Setting up Next.js

Awesome! So far so good! With our NestJS app set up, we can now move on to creating our Next.js app inside our apps directory, which we'll name—drumroll—frontend. All we have to do is run the following command to create the project:

npx create-next-app@latest frontend --typescript
Enter fullscreen mode Exit fullscreen mode

Similar to our NestJS app, we also want to use TypeScript in our Next.js app. After running the command, you'll be presented with configuration options. I've opted to stick with the default config settings, but feel free to customize them based on your requirements.

Next.js installation config

Once installation and set up is done, navigate into the frontend directory, delete the .git folder and .gitignore file—we'll set up git in the root level of our repository, and start a server to run our newly created Next app:

cd frontend && npm run dev
Enter fullscreen mode Exit fullscreen mode

Our Next.js app is now running at http://localhost:3000. Remember when we mentioned port 3000 earlier? It’s the default port for Next.js applications. Open your browser and enter http://localhost:3000 in the url bar. You should see the following:

Next.js Home Page

Looks great! Here's how our directory tree looks like now with both our NestJS and Next.js apps set up:

Directory tree

Running Both Apps Together

Right now, both apps are up and running, but separately. We’re aiming for teamwork here though—we want to launch them together with a single command from the root directory. To make that happen, we’ll need to tweak a few settings. Once we’ve done that, boom—one command, both apps up and running in sync.

To achieve this, we'll need to install a package called concurrently in the package.json file at the root level of our repository.

concurrently, as its name insinuates, allows us to run multiple commands simultaneously in a single terminal window. This is particularly useful for monorepos where you want to start multiple services or apps at once. By using concurrently, we can run both the NestJS and Next.js apps in parallel, making it easy to manage them with a single command. Let's install it by running the following command:

npm i concurrently
Enter fullscreen mode Exit fullscreen mode

We still need to tweak one or two things before we can run both our apps together using a single command. In the package.json file in our project root, we'll need to add the following lines in the "scripts" section.

"dev": "concurrently \"npm:dev:backend\" \"npm:dev:frontend\"",
"dev:backend": "cd apps/backend && npm run start:dev",
"dev:frontend": "cd apps/frontend && npm run dev",
Enter fullscreen mode Exit fullscreen mode

Our package.json file should now look like this:

{
  "name": "another-juan",
  "version": "1.0.0",
  "description": "This is a project aimed at providing more insight on how to set up a monolithic repository that consists of both Next.js and NestJS apps.",
  "main": "index.js",
  "scripts": {
    "dev": "concurrently \"npm:dev:backend\" \"npm:dev:frontend\"",
    "dev:backend": "cd apps/backend && npm run start:dev",
    "dev:frontend": "cd apps/frontend && npm run dev",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Shaquille Ndunda",
  "license": "ISC",
  "dependencies": {
    "concurrently": "^9.0.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

And voila! Let's execute the following command in our project/repository root:

npm run dev
Enter fullscreen mode Exit fullscreen mode

In your browser, go to http://localhost:3000 for the frontend app, and http://localhost:3001 for the backend app. Works like a charm. Check out our terminal:

Server running

Initializing Git and Pushing to GitHub

Now that we've successfully set up our monorepo, last thing we're going to do is initialize git in our monorepo. Make sure you at least have git set up in your local development environment, and a GitHub account.

To set up git, run the following command in the project root:

git init
Enter fullscreen mode Exit fullscreen mode

This will automatically create a .git folder in your root. We however do not want to push everything to our remote repository and so we need to create a .gitignore file—still in our project root. Inside it, add the following:

node_modules
dist
build
out
.env
.next
*.tsbuildinfo
next-env.d.ts
.pnp
.pnp.js
.yarn/install-state.gz
.DS_Store
*.pem
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Enter fullscreen mode Exit fullscreen mode

Then head over to your GitHub account and create a new repository. Choose an owner, add a description, select visibility (public or private), and add a README.md file (useful especially if you're in a team or collaborating with someone else). Click on Create repository.

Back in your local environment, inside your project root, run the following command to link your local repository to the remote repository you just created on GitHub:

git remote add origin [repository_url]
Enter fullscreen mode Exit fullscreen mode

Run this to pull the remote repository into your local repository:

git pull origin main
Enter fullscreen mode Exit fullscreen mode

Finally, stage your files, commit them, and push.

git add .
git commit -m [commit message]
git push -u origin main
Enter fullscreen mode Exit fullscreen mode

Next Steps

Congratulations! You just created and set up a Next.js and NestJS monorepo. In the next article, we'll explore more into custom configuration for both apps, build a simple CRUD operations project, get into database integration, and UI development.

If you want to follow through this tutorial with the source code:

GitHub logo shaqdeff / another-juan

This is a project aimed at providing more insight on how to set up a monolithic repository that consists of both Next.js and NestJS apps.

Next.js and NestJS Monorepo

This project demonstrates a monorepo setup combining Next.js for the frontend and NestJS for the backend. It's designed to provide insights on how to structure and manage a full-stack TypeScript application using these two powerful frameworks.

Project Structure

  • apps/frontend: Next.js application
  • apps/backend: NestJS application
  • Root level configuration for the monorepo

Description

This monorepo showcases:

  • A Next.js frontend with Tailwind CSS for styling
  • A NestJS backend API
  • Concurrent development of both frontend and backend
  • Shared configuration and dependencies management

Getting Started

Prerequisites

  • Node.js (version 14 or later recommended)
  • npm or yarn

Installation

  1. Clone the repository:

    git clone https://github.com/shaqdeff/another-juan.git
    cd another-juan
    Enter fullscreen mode Exit fullscreen mode
  2. Install dependencies:

    npm install
    Enter fullscreen mode Exit fullscreen mode

Running the Development Environment

To start both frontend and backend concurrently:

npm run dev
Enter fullscreen mode Exit fullscreen mode
  • This command utilizes the script defined in the root package.json file to run the dev script in both apps/frontend and apps/backend directories.

Accessing the Applications





You can also follow me here on Dev.to to stay updated on the next articles, and other tutorials/articles.

.
Terabox Video Player