SPA Mode

Static export served by NestJS: single process, single port

If your app doesn't need server-side rendering, you can build Next.js as a static export and serve it directly from NestJS. This means a single process and a single port in production: no reverse proxy, no separate web container.

NestJS · port 3000/api/*controllers/*static files (Next.js build)

How it works

next build with output: "export" emits static HTML/JS/CSS to apps/web/out/. NestJS uses ServeStaticModule to serve those files, while still handling /api/* routes through its controllers.

output: "export" is incompatible with rewrites(), so the config splits by environment, with rewrites in dev for the local proxy and static export in production:

next.config.js

apps/web/next.config.js
const isProduction = process.env.NODE_ENV === 'production';
 
const nextConfig = {
  ...(isProduction
    ? { output: 'export' }
    : {
        async rewrites() {
          return [
            {
              source: '/api/:path*',
              destination: 'http://localhost:3001/api/:path*',
            },
          ];
        },
      }),
};

app.module.ts

apps/api/src/app.module.ts
import { join } from 'path';
import { ServeStaticModule } from '@nestjs/serve-static';
 
@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(process.cwd(), '../web/out'),
      exclude: ['/api/(.*)'],
    }),
    // ...
  ],
})
export class AppModule {}

Install the module:

pnpm add @nestjs/serve-static --filter=api

Build and run

# 1. Build the static frontend
cd apps/web && next build
 
# 2. Build the API
cd apps/api && nest build
 
# 3. Start NestJS - it serves everything on port 3000
PORT=3000 node apps/api/dist/main

In production, only the NestJS container needs to run. The Next.js build output in out/ should be present at ../web/out relative to where NestJS starts.

Limitations

No SSR, no Server Components with request-time data, no cookies() or headers() in server code. Static export is a build-time-only rendering model. If any page uses these features, next build will throw an error.