Nestjs で @angular/ssr すると ES Module 周りでエラーが出る話
はじめに
Nestjs は2024年7月現在、ES Moduleの対応がないので @angular/ssr がそのまま import できないよ。という話です。
やりたい構成
Angularは標準のCLIから生成されるSSRのコードはexpressベースです。
ただし、@angular/ssr 自体にフレームワーク依存があるわけではないので、NestJSでも使いたくなるわけです。
今回はNestjsのプロジェクト内にAngularのプロジェクトを入れ込み localhost:3000/
にアクセスされた時にAngularのSSRされたWebページを返す構成を考えていきます。
$ tree
.
├── README.md
├── dist
├── nest-cli.json
├── node_modules
├── package-lock.json
├── package.json
├── src
│ ├── angular-frontend # ここにAngularのファイル
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
├── tsconfig.build.json
└── tsconfig.json
Nestjs 内に Angular のプロジェクトを作る
Nestjs も Angular も CLI があるので、コードを生成していきます。
$ nest new nestjs-in-angular18-with-ssr
$ cd nestjs-in-angular18-with-ssr/src
$ ng new angular-frontend
Nestjs と Angular では Typescript の compilerOptions が何から何まで異なるため、Nestjs側の tsconfig.build.json
に対して exclude
の追加を行います。
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts", "src/angular-frontend"]
}
Nestjs に Angular SSR の設定
Nestjs で Angular SSR の設定を行います。
必要なモジュールをインストールします。
$ cd .. # nestjs-in-angular18-with-ssr のプロジェクトルートでインストール
$ npm install --save @nestjs/serve-static
$ npm i @angular/ssr
基本的には Angular CLI が生成する server.ts
のコードを Nestjs 側に移植するイメージです。
Nestjsの main.ts
を修正します。
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.set('view engine', 'html');
app.set('views', './angular-frontend/dist/angular-frontend/browser');
await app.listen(3000);
}
bootstrap();
Nestjs 側の app.module.ts
に static コンテンツのパスを指定しておきます。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, 'angular-frontend', 'dist', 'angular-frontend', 'browser'),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
いよいよ、Nestjs に Angular SSR の設定を追加します。
こちらも Angular CLI が生成する server.ts
のコードを Nestjs 側に移植します。
簡略化のため controller に全てのコードを書いています。
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
import { AppService } from './app.service';
//
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import bootstrap from './angular-frontend/src/main.server';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
@Controller()
export class AppController {
commonEngine = new CommonEngine();
constructor(private readonly appService: AppService) {}
@Get('')
frontend(@Req() req: Request): any {
const serverDistFolder = dirname(fileURLToPath('./angular-frontend/dist/angular-frontend/server'));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const { protocol, originalUrl, baseUrl, headers } = req;
this.commonEngine.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
});
}
@Get('hello')
getHello(): string {
return this.appService.getHello();
}
}
発生するエラー
Nestjs の開発環境を起動すると以下のようなエラーが吐かれます。
$ npm run start
> nestjs-in-angular18-with-ssr@0.0.1 start
> nest start
node:internal/modules/cjs/loader:1205
throw new ERR_REQUIRE_ESM(filename, true);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module /<PROJECT PATH>/nestjs-in-angular18-with-ssr/node_modules/@angular/common/fesm2022/common.mjs not supported.
Instead change the require of /<PROJECT PATH>/nestjs-in-angular18-with-ssr/node_modules/@angular/common/fesm2022/common.mjs to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (/<PROJECT PATH>/nestjs-in-angular18-with-ssr/dist/app.controller.js:18:18) {
code: 'ERR_REQUIRE_ESM'
}
Nestjs は ESM のサポートが現時点でないようです。
また、Nestjs は nestjs/ng-universal というライブラリを提供していましたが、こちらも Angular17 以降、動かない状況です。
まとめ
Nestjs は2024年7月現在、ES Moduleの対応がないので @angular/ssr がそのまま import できないよ。という話でした。
ご参考までに作ったレポジトリはこちら
Discussion