この記事は NestJS アドベントカレンダー 2019 6 日目の記事です。
はじめに
この記事では、アプリケーションをプロダクションとして動かす上で必要な手順のうち、いくつかを紹介します。
サンプルコードのリポジトリは以下になります。
https://github.com/nestjs-jp/advent-calendar-2019/tree/master/day06-prepare-for-production-usage
なお、環境は執筆時点での Node.js の LTS である v12.13.1 を前提とします。
cli で雛形アプリケーションを作成
この記事では @nestjs/cli
で生成される雛形に対して、プロダクションで実行するための設定を加えてゆきます。
1$ nest new day6-prepare-for-production-usage
config を作る
公式ドキュメントの Configuration の項では、環境変数を活用するのが良いと説明されています。
重厚にやる場合はドキュメントのように dotenv
等を使うのが良いですが、このサンプルでは小さいので、 NODE_ENV での分岐をベースにした config ファイルを作成します。
1import { LogLevel } from '@nestjs/common';
2
3interface Config {
4 logLevel: LogLevel[];
5}
6
7const develop: Config = {
8 logLevel: ['debug', 'log', 'verbose', 'warn', 'error'],
9};
10const production: Config = {
11 logLevel: ['log', 'verbose', 'warn', 'error'],
12};
13
14export const config =
15 process.env.NODE_ENV === 'produiction' ? production : develop;
アプリケーションの logger を設定する
NestFactory.create()
の引数にオプションを渡すことで、 logger のログレベルを設定できます。先ほどの config を用いて設定してみます。
また、 app.useLogger
を指定することで、 logger を指定することができます。
デフォルトで NestJS の提供する logger を使っているのですが、次で使用するので明示的に宣言しておきます。
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule, { logger: config.logLevel });
3 const logger = new Logger();
4 app.useLogger(logger);
5
6 await app.listen(3000);
7}
middleware にリクエストロガーを設定する
NestJS はデフォルトの場合は express のエンジンを使用するため、 express の作法で middleware を記述することができます。
request-logger.middleware.ts
1import {
2 Request as ExpressRequest,
3 Response as ExpressResponse,
4} from 'express';
5
6export function requestLogger(
7 logger: any,
8): (req: ExpressRequest, res: ExpressResponse, next: () => void) => void {
9 return (req, res, next): void => {
10 res.on('finish', (): void => {
11 logger.info(`${req.method} ${req.url} -> ${res.statusCode}`);
12 });
13 next();
14 };
15}
middleware の設定も express と同じように app.use()
で設定することができます。
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule, { logger: config.logLevel });
3 const logger = new Logger();
4 app.useLogger(logger);
5 app.use(requestLogger(logger));
6
7 await app.listen(3000);
8}
CORS の設定
NestJS の標準設定では CORS は不許可なので、別のドメインからのアクセスを弾きます。
別ドメインにホスティングしたフロントエンドから NestJS アプリケーションの API を叩けるようにするためには、 CORS を有効にする設定が必要です。
試しに、 fetch を行うだけの html を作り、そこから Nest アプリケーションの API を叩いてみます。
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <title>CORS sample</title>
6 <script>
7 fetch('http://localhost:3000')
8 .then(res => res.text())
9 .then(text => console.log(text));
10 </script>
11 </head>
12 <body></body>
13</html>
NestApplication での CORS を許可する設定は 2 種類あります。両方紹介します。
NestFactory.create()
に { cors: true }
のオプションを渡す。
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule, { cors: true });
3 const logger = new Logger();
4 app.useLogger(logger);
5 app.use(requestLogger(logger));
6
7 await app.listen(3000);
8}
app.enableCors()
する。
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule);
3 const logger = new Logger();
4 app.useLogger(logger);
5 app.use(requestLogger(logger));
6 app.enableCors()
7
8 await app.listen(3000);
9}
それぞれ、オプションとして CORS の設定が渡せます。デフォルトでは全許可なので、必要に応じて絞り込んでください。
cors-oprions.interface.d.ts
1export interface CorsOptions {
2 /**
3 * Configures the `Access-Control-Allow-Origins` CORS header. See [here for more detail.](https://github.com/expressjs/cors#configuration-options)
4 */
5 origin?: boolean | string | RegExp | (string | RegExp)[] | CustomOrigin;
6 /**
7 * Configures the Access-Control-Allow-Methods CORS header.
8 */
9 methods?: string | string[];
10 /**
11 * Configures the Access-Control-Allow-Headers CORS header.
12 */
13 allowedHeaders?: string | string[];
14 /**
15 * Configures the Access-Control-Expose-Headers CORS header.
16 */
17 exposedHeaders?: string | string[];
18 /**
19 * Configures the Access-Control-Allow-Credentials CORS header.
20 */
21 credentials?: boolean;
22 /**
23 * Configures the Access-Control-Max-Age CORS header.
24 */
25 maxAge?: number;
26 /**
27 * Whether to pass the CORS preflight response to the next handler.
28 */
29 preflightContinue?: boolean;
30 /**
31 * Provides a status code to use for successful OPTIONS requests.
32 */
33 optionsSuccessStatus?: number;
34}
ビルドとプロダクション実行
@nestjs/cli
の nest start
コマンドは、内部で TypeScript をコンパイルしてから実行しているため、起動が遅くなっています。
開発時は nest start --watch
を使用することで自動でビルド 〜 再起動までしてくれるため回避できますが、プロダクションでは、特にクラウドネイティブな環境では起動が遅いことがパフォーマンスのネックとなることが往々にしてあります。
本来の TypeScript のアプリケーションと同様にビルドして実行するために、 @nestjs/cli
では、 nest build
コマンドが用意されています。
標準では dist ディレクトリにファイルが吐き出されるため、その中にある main.js
を実行します。
1$ yarn build
2$ node dist/main.js
終わりに
この記事では、アプリケーションをプロダクションとして動かす上で必要な手順のうち、いくつかを紹介しました。全てではありませんが、 express をベースとした手法は上記の方法でほとんど実現できると思われます。
明日は @potato4d さんによる、サンプルアプリケーションの実装です。