How to setup a Next.js and NestJS Monorepo (For Dummies) š¤
Shaquille Ndunda
Posted on September 18, 2024
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!
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 it 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:
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
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
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
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.
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.
To start the project, simply run the following command in your terminal:
npm run start
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:
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
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.
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
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:
Looks great! Here's how our directory tree looks like now with both our NestJS and Next.js apps set up:
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
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",
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"}}
And voila! Let's execute the following command in our project/repository root:
npm run dev
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:
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
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:
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]
Run this to pull the remote repository into your local repository:
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:
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
Clone the repository:
git clone https://github.com/shaqdeff/another-juan.git
cd another-juan
Install dependencies:
npm install
Running the Development Environment
To start both frontend and backend concurrently:
npm run dev
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.