This is the first story of series Real Dev Engineering (How Real Dev is built). In this story, we talk about NextJS and NestJS, what are they, how to integrate them together.

Say again. What is which?

I know, it’s a bit confusing :) I’ll give a little bit context. If you know what they are, feel free to skip this part.

NestJS is “a progressive Node.js framework to building efficient, reliable and scalable server-side applications”. Too abstract? It’s a framework for building backend, like Django for Python, or Spring Boot for Java.

You might ask, what about Express or Koa? Express is more like Flask in Python. It only handles the HTTP parts without giving you the “batteries”, like Authentication, ORM, Real “Middleware” (Express’s middleware is a different concept), Caching, so on and so forth. It doesn’t have a lot of best practice either.

NestJS, on the other hand, enforces a coding structure (inspired by Angular) and has best practice around all the above things. In fact, it uses Express as a backend, so everything is compatible. You can replace the backend with fastify if you want performance.

NextJS is the “battery-included” version of React for the frontend. The framework handles the following things:

Server-Side Rendering: Benefits SEO and initial loading time

Routing

Bundling your CSS, JS and loading them dynamically

CSS support is native (and will be builtin)

Both of these frameworks are open source and have full support for TypeScript. There are already tons of successful in-production use cases.

Getting started

Our codebase is like the following structure, a bit unusual, but it doesn’t affect how to set it up.

Codebase folder structure

Typically with NextJS, you can use its embedded webserver as your backend (Express based), but it’s fairly limited. Most people use a hand written server (also mostly Express based 😆), and just use the Next “server” for its rendering purpose. Think of calling a render function. Our goal here is to replace that custom Express with NestJS.

In order to put Nest in the picture, we create a webserver using NestJS. It internally maintain an instance of Next server. Whenever a request like https://real.dev/tasks comes:

NextJS server takes it, sees it as a non-API request, route it to NextController (see below)

(see below) NextController calls its internal render function

calls its internal function Render function looks for task.tsx in the pages folder

in the folder The default-exported React component from task.tsx is rendered, and returned as response

Next module in Nest

NestJS organizes code into modules (like the task module above). We’ll create a dedicated module for interacting with the Next server.

We create a next server instance and provide it to the controllers to use.

We have to use FactoryProvider since Next server needs to be prepare() ed before usage.

I’ll omit the definition of nextConfig since it’s a typical config object, except in our case, we wrote it in TypeScript with proper type annotation to ensure correctness. One caveat is about dir and distDir , which we’ll talk about later.

The controller is straightforward: just forward the request into the Next server.

Next up, we’ll go into two detailed issues we had with this approach.

Next’s build config

NextJS takes a config either from constructor or next.config.js . In there, two configs regards to directory in NextJS, which are not very well documented. If you have a standard setup, it probably “just works”. But it’s generally useful to know about them.

dir : The source directory. You can simply think it’s the directory containing the pages folder. It defaults to . - the current folder. This is also used when you do next build <dir> . In our case, it’s packages/website

: The source directory. You can simply think it’s the directory containing the folder. It defaults to - the current folder. This is also used when you do . In our case, it’s distDir : The relative path from the source folder to the build output. Defaults to .next . That’s why, by default, the built asset will go into ./.next . In our case, it should be set to something like path.relative('packages/website', <BUILD_DIR>) . This is because we have a dedicated build directory, and they’re separate for development and production build.

I learned this by looking at this code. With the proper settings in next.config.ts , we should be able to spin up a server in development!

One more thing to be done. For prod build, we run next build packages/website . It’ll look for a file named next.config.js under the dir , and an config object in there. We don’t use this file for development because next server builds on the fly on development, and takes the config from our next.config.ts (not JS!). In order to share the config, we can easily do something like:

Here the required path points to the compiled JS output from TSC. It should be typical if the project is setup in a NPM publish-able way. (We’re actually not, but that’s going to be another story 😛)

Hot reloading all the stuff

Till now, we have a working integration of Next + NextJS. It works both on development and production. One more thing.

NestJS documentation has a nice little trick: Hot reload the server using webpack. I would recommend everyone trying it.

It basically means every change on your server code, takes a glance to reload, instead of waiting 5 seconds for the server to restart. It works in a non-intrusive way, meaning it doesn’t change anything existing. How awesome is that! Following the doc to set it up.

One issue after we have NextJS in the picture though: The provider will be reloaded when the app instance is closed. That means the Next server instance is re-created, hence the in-memory compile cache of pages and components are lost. Even though the backend server takes less than a second to reload, NextJS will recompile all the frontend stuff, which takes another 3–4 seconds. It shoots us back to square one!

The solution is actually pretty straightforward: Cache the server object in the provider.

That’s it. We have a fully hot-reloading server, which reloads within one second, no matter it’s a backend or a frontend change.

Conclusion

These are all the things I’ve done to set up a backend+frontend project, with the following goodies:

Nice coding structure

SSR support out of the box

Hot reloading any change

This is by no means a perfect solution. There’s other ways of doing the integration, for example the nest-next in reference. If you see anything that can be better, please do let me know!

Hope you like it! Stay tuned for more stories.

P.S: I’m happy to talk about details or tech in general, reply here or drop me an email at luke@real.dev.

About Us @ Real Dev

Real Dev is a community of developers with practical engineering skills. We are committed to help developers to (im)prove themselves, and companies to hire talented developers.

You can showcase your skills by solving our tasks, and we help you find dev jobs looking for exact able developer like you! Try it out!

References