Nxではじめる Angular + NestJS フルスタックWebアプリ開発

この記事は [Angular #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/angular-2) 9日目の記事です。

今年のng-japanで発表されていた「Angular + NestJS」に興味を持ったのでやってみました。
https://www.youtube.com/watch?v=2QTumr8-KhU

[NestJS](https://nestjs.com/)はAngularに影響を受けて作られたサーバーサイドアプリケーションフレームワークです。フロントエンド・バックエンド両方を同じ感覚で書けるのは良いですよね。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106886/59c25997-bcdb-2b6e-fec9-6a41aad271a5.png)
ng-japanの発表ではLernaを使ったモノレポ構成となっていましたが、最近だと[Nx](https://nx.dev/)を使うのも良さそうだったので、サンプルを交えながら解説していこうと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106886/4bd1ab4c-c8b2-2038-e867-f1c31dc11399.png)

## ワークスペースの作成
`create-nx-workspace`コマンドでワークスペースを生成します。

```bash
$ npx create-nx-workspace
```

ワークスペース名を指定し、テンプレートとして「angular-nest」を選択しましょう。

```
$ npx create-nx-workspace
? Workspace name (e.g., org name)     workspace
? What to create in the new workspace 
  empty             [an empty workspace] 
  web components    [a workspace with a single app built using web components] 
  angular           [a workspace with a single Angular application] 
❯ angular-nest      [a workspace with a full stack application (Angular + Nest)]
  react             [a workspace with a single React application] 
  react-express     [a workspace with a full stack application (React + Express)
(Move up and down to reveal more choices)
? Application name                    web
? Default stylesheet format           SASS(.scss)  [ http://sass-lang.com ]
```

なんとたったこれだけで「Angular + NestJS」の環境ができました。

## アプリケーションの起動
`nx serve <プロジェクト名>`で各アプリケーションを起動できます。

```bash
$ npm run nx serve api
$ npm run nx serve web
```

## プロジェクト構成
生成されたプロジェクトには`apps`ディレクトリにフロントエンド・バックエンドそれぞれのアプリケーションが入っており、`libs/api-interfaces`内に共通のインターフェースが定義されています。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106886/b30558fe-eb2d-371c-fd81-5b1aaa874295.png)
`tsconfig.json`でパスのエイリアスも設定済みなので、インポートの際は`@workspace/api-interfaces`を指定すればOKです。

```ts:apps/web/src/app/app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Message } from '@workspace/api-interfaces';

@Component({...})
export class AppComponent {
  hello$ = this.http.get<Message>('/api/hello');
  constructor(private http: HttpClient) {}
}
```

## アプリケーションの追加

下記のコマンドでアプリケーションを追加できます。

```bash
$ npm run ng g @nrwl/angular:app <アプリケーション名>
```

生成されたアプリケーションは、ユニットテストとE2Eテストにそれぞれ[Jest](https://jestjs.io/)、[Cypress](https://www.cypress.io/)をデフォルトで使用する設定となっています。

## ライブラリの追加

下記のコマンドでライブラリを追加できます。アプリケーション間で共通で使うサービスやコンポーネントを入れると良いでしょう。

```bash
$ npm run ng g @nrwl/angular:lib <ライブラリ名>
```

`tsconfig.json`のエイリアス設定もライブラリ生成時に自動で追加されます。

```json:tsconfig.json
{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    (中略)
    "baseUrl": ".",
    "paths": {
      "@workspace/api-interfaces": ["libs/api-interfaces/src/index.ts"],
      "@workspace/ui": ["libs/ui/src/index.ts"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}
```

## Schematicの追加

追加のインストールや設定無しでSchematicを作成・実行できるのもNxの面白いところです。

```bash
$ npm run ng g workspace-schematic
```

`ng g workspace-schematic`コマンドを実行すると`shcema.json`と`index.json`が生成されます。

以下の例では`src/environments/envirnment.ts`を生成するSchematicを示しています。APIキーが含まれているなど、ビルドに必要でもバージョン管理下に置きたくない場合はSchematicを使って生成するのも一つの手かと思います。

```json:tools/schematics/environment/schema.json
{
  "$schema": "http://json-schema.org/schema",
  "id": "environment",
  "type": "object",
  "properties": {
    "project": {
      "type": "string",
      "description": "Name of the project.",
      "x-prompt": "Which project would you like to add environment variables?"
    },
    "production": {
      "type": "boolean",
      "description": "Production",
      "default": false
    },
    "apiToken": {
      "type": "string",
      "description": "API token"
    },
  },
  "required": ["project"]
}
```

```ts:tools/schematics/environment/index.ts
import { strings } from '@angular-devkit/core';
import { Rule, SchematicContext, apply, applyTemplates, mergeWith, mov, url } from '@angular-devkit/schematics';
import { toFileName } from '@nrwl/workspace';

export interface Schema {
  project: string;
  production: boolean;
  apiToken?: string;
}

export default function(schema: Schema): Rule {
  const name = toFileName(schema.production ? 'environment.prod' : 'environment');
  const directory = `apps/${schema.project}/src/environments`;
  return (_, _context: SchematicContext) => {
    return mergeWith(apply(url('./files'), [
      applyTemplates({
        ...strings,
        name,
        production: schema.production,
        apiToken: `'${schema.apiToken || ''}'`,
      }),
      move(directory)
    ]));
  };
}
```

```ts:tools/schematics/environment/files/__name__.ts.template
export const environment = {
  production: <%= production %>,
  apiToken: <%= apiToken %>
};
```

https://gist.github.com/puku0x/8b4a458be87d2cdef463f1eb2ce406b7

## アプリケーションのテスト
`nx test <プロジェクト名>`でユニットテスト、`nx e2e <プロジェクト名>-e2e`でE2Eテストを実行します。

```bash
$ npm run nx test web
$ npm run nx e2e web-e2e
```

`nx affected:test`コマンドを使うとNxが自動で差分を検出して必要なテストを実行してくれます。CI環境などで実行時間を最適化したい場合にオススメです。

```bash
$ npm run nx affected:test  # nx affected -- --target=testでも可
>  NX   NOTE  Affected criteria defaulted to --base=master --head=HEAD
>  NX  Running target test for projects:
  - api-interfaces
  - web
  - api
  - ui
```

## アプリケーションのビルド
アプリケーションをビルドするには`nx build <プロジェクト名>`を実行します。プロダクションビルドの場合は`--prod`オプションを指定します。

```bash
$ npm run nx build api -- --prod
$ npm run nx build web -- --prod
```

## おわりに

バックエンドにNestJSを採用するとAngularと知見を共有できるの効率的に開発できそうです。さらにNxを利用することで、Angular + NestJSの環境を簡単に作成できます。Angular + NestJSでフルスタックWebアプリ開発したい場合はNxを試してみると良いと思います。