Computing Atman
How to Serve Next.js SSG (Static Site Generation) from Nx NestJS
🖼

How to Serve Next.js SSG (Static Site Generation) from Nx NestJS

This article explains how to use the monorepo tool Nx to serve a statically generated site created with Next.js SSG (Static Site Generation) from a Next.js application. With this approach, you can have both the frontend and backend in a single project.

2023/09/24
2023/09/24

Introduction

In this article, we will explain how to use the monorepo tool Nx to serve a statically generated site created with Next.js SSG (Static Site Generation) from a Next.js application. This approach allows you to have both the frontend and backend in a single project.

1. Create Frontend and Backend Projects

Create Nx Workspace and Nestjs Project

Navigate to the desired directory and run the following command:

npx create-nx-workspace@latest
 
✔ Where would you like to create your workspace? · sample-workspace
✔ Which stack do you want to use? · node
✔ What framework should be used? · nest
✔ Integrated monorepo, or standalone project? · integrated
✔ Application name · api
✔ Would you like to generate a Dockerfile? [https://docs.docker.com/] · No
✔ Enable distributed caching to make your CI faster · No

This will create the Nestjs api project.

Create a Nextjs Project

nx g @nx/next:app web
 
✔ Which stylesheet format would you like to use? · css
✔ Which E2E test runner would you like to use? · cypress
✔ Would you like to use the App Router (recommended)? (Y/n) · true

The directory structure created by the above commands will look like this:

sample-workspace/
  |- apps/
  |    |- api/
  |    |- web/

2. Enable Nextjs (Frontend) for SSG (Static Site Generation)

To enable support for static files, make the following changes:

  • Add unoptimized: true in images (SSG doesn't support next/image).
  • Add output: 'export'.
  • Add trailingSlash: true.
const nextConfig = {
  nx: {
    // Set this to true if you would like to use SVGR
    // See: https://github.com/gregberge/svgr
    svgr: false
  }, 
  images: {
    unoptimized: true,
  }, 
  output: 'export', 
  trailingSlash: true
}; 

If you don't set trailingSlash: true, when accessing Nextjs via Nestjs, reloading the browser on pages other than the homepage will redirect you to the homepage.

NEXT.JS trailingSlash

3. Add ServeStaticModule to Nestjs (Backend)

Import the serve-static Package

npm i @nestjs/serve-static

Implement the ServeStaticModule

import { ServeStaticModule } from '@nestjs/serve-static'; 
import path, { join } from 'path'; 
// Other standard imports are omitted
 
@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'web/.next'),
      exclude: ['/api/*']
    }),
  ], 
  controllers: [AppController], 
  providers: [AppService]
})
export class AppModule {}

With the above code, if the endpoint does not start with /api, it will redirect to the .next directory, which is the static site of Nextjs.

At this point, the .next folder has not been generated yet. It will be generated later when you run Nextjs Build, as explained below.

4. Add Build and Start Commands to package.json

Add the build and start commands to your package.json file.

"scripts": {
    "cd:build-api": "npx nx build api --prod",
    "cd:build-web": "npx nx build web --prod",
    "cd:build": "npm run cd:build-api && npm run cd:build-web",
    "cd:web-start:server": "cd dist/apps/web && npm run start",
    "cd:web-start:static": "npx serve dist/apps/web/.next",
    "cd:api-start": "node dist/apps/api/main.js"
}

Next, execute the build command:

npm run cd:build

This will generate build files for both the frontend (web) and backend (api).

Finally, execute the start command:

npm run cd:api-start

This will start the backend server on localhost, and you can access the Next.js pages in your browser.

By deploying these, you can serve both the frontend and backend integrated on a single server.

For reference, here is the directory structure after the build:

sample-workspace/
  |- apps/
  |    |- api/
  |    |- web/
  |
  |-dist/  <= generated after the build
  |   |- apps/
  |        |- api/   
  |        |- web/
  |             |- .next/
  |
  |- package.json