Help us understand the problem. What is going on with this article?

Angular+Nest.jsのモノリスプロジェクトを、Lernaを使いプロジェクト内でクライアント/サーバーのコード分割をしてみる

現在、Angular+Express.jsのモノリスな環境で作業を行っているのですが、クライアントサイド/サーバーサイドともにコードが大きくなってきて、メンテナンスが大変になってきました。。。

先日、ng-japan2019に参加した際に、Angular+Nest.jsプロジェクトをLernaでモノリポ管理しているという話を聞き、これは良さそうだということで試してみることにしました。

※ng-japan2019の発表の様子はYouTubeで見ることができます。
NestJS + TypeORM + Angularで作るPure TypeScriptなアーキテクチャ by 白石 俊平

Express.js⇒Nest.jsへの載せ替えもしたいので、お試しがてらAngular+Nest.jsのモノリスプロジェクトをクライアント/サーバーでソースコードを分けつつ、モノリポで管理できるようにしたいと思います。

環境

VirtualBox + VagrantでゲストOSとしてCentOSのイメージを使っています

Lernaのインストール

公式のGetting Startedに従って、Lernaのインストールおよびテンプレートの作成を行います。

$ mkdir lerna-repo && cd $_
$ npx lerna init

いちいちnpxで実行するのはめんどくさいのでグローバルにLernaをインストールします。

npm install -g lerna

クライアント/サーバー用のパッケージを追加

まずはクライアントサイドのパッケージを追加します。

lerna create client

色々聞かれますが、とりあえず何も入力せずEnter連打します。

続いてサーバーサイドのパッケージを追加します。

lerna create server

こちらも同様にEnter連打します。

以下のようなディレクトリとファイルが作成されます。

image.png

Nest.jsプロジェクト作成

Nest.jsのCLIをインストールしていない場合はインストールします。

npm install -g @nestjs/cli

Nest.jsのテンプレートプロジェクトを生成し、Lernaパッケージを追加した場所にコピーします。

nest new server
mv server/* packages/server/

Angularプロジェクト作成

AngularのCLIをインストールしていない場合はインストールします。

npm install -g @angular/cli
ng new client
mv client/* packages/client/

動くようにする

今のままだと、単純にNest.jsとAngularのプロジェクトがそれぞれ追加されただけなので、動くように修正していきます。

ビルド先の設定

クライアント/サーバーともにdistディレクトリにビルドファイルの出力を行うため、親の方にdistディレクトリを作成し、シンボリックリンクを作成します。

※この設定(シンボリックリンク)がなくても後述の「Nest.js ⇔ Angular連携」で直接packages/client/distを参照するようにすればいけます。

mkdir dist
ln -s ../packages/client/dist/client dist/client
ln -s ../packages/server/dist dist/server

※lerna-repoディレクトリ直下で実行します

試しにビルドしてみます

lerna run build

⇒クライアント/サーバー両方のnpm run buildが実行され、lerna-repo/dist内は以下のようになります。

image.png

起動設定

親のpackage.jsonに起動コマンドを追加します。

lerna-repo/package.json
"scripts": {
  "start": "node dist/server/main.js"
}

この状態でnpm startを実行するとサーバーが起動します。
が、このままだと、ただNest.jsのAPIを呼ぶだけでAngularが動かないので連携させます。

Nest.js ⇔ Angular連携

サーバーサイドでAngularコードを参照できるようにします。
また、APIのURLは/api/~となるようにプレフィクスを設定しておきます。

lerna-repo/server/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// 追加 start
import * as express from 'express';
import { join } from 'path';
// 追加 end

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

// 追加 start
  app.setGlobalPrefix('api');

  app.use(express.static(join(__dirname, '..', 'client')));
  app.use('/*', express.static(join(__dirname, '..', 'client/index.html')));
// 追加 end

  await app.listen(3000);
}
bootstrap();

Angularとの連携に必要な作業はこれだけです!

起動してみる

npm startで起動し、ブラウザでアクセスします!

image.png
※VMを192.168.33.10で立ち上げてます。

無事、Nest.jsにアクセスしてAngularの画面を表示することができました!!!
ちなみにhttp://192.168.33.10:3000/apiにアクセスすると、Nest.js側のAPIを呼び出せます。(Hello World!!が返ってくるだけ)

AngularからNest.jsのAPIを呼んでみる

せっかくなので、AngularからNest.jsのAPIを呼んでみます。

HttpClientを使うのでModuleをインポートします。

lerna-repo/packages/client/src/app/app.module.ts
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    ・・・
    HttpClientModule
  ],
})
export class AppModule { }

app.component.tsのngOnInitでHttpClientを使ってAPIを呼び出してtitleにセットします。

lerna-repo/packages/client/src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'client';

  constructor(private http: HttpClient) { }

  async ngOnInit() {
    const result = await this.http.get('/api').toPromise();
    this.title = result['message'];
  }
}

サーバーサイドはJSON形式でオブジェクトで返却するようにします。

lerna-repo/packages/server/src/app.controller.ts
  @Get()
  getHello() {
    return { message: this.appService.getHello()};
  }

再度ブラウザで確認

サーバーを再起動し、再度ブラウザで確認してみます。

lerna run build
npm start

image.png

ちゃんとタイトル部分が「Hello World!!」に変わってます:grin:

まとめ

これで、サーバーサイドとクライアントサイドのコード管理を分けつつ、モノリスな環境で動かすことが出来ました!
コード分割により、package.jsonがスッキリしたり、コードが見やすくなったり、と結構いい感じです:smiley:

デバッグをどうするかとかも考えないといけないですが、それはおいおいやって行こうかと思います。

参考

Lerna
ng-japan2019
NestJS + TypeORM + Angularで作るPure TypeScriptなアーキテクチャ by 白石 俊平

teracy55
2011年からエンジニアやってます。 最近はAngular/NestJSをメインに Webシステム作ってます。それ以外だと、組み込みエンジニアとしてC/C++でカーナビ開発、Javaで業務システム、PHPでのWebシステム開発、Android/iOSアプリ開発(Flutter、Monaca、CocosCreator)なんかをやってきました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away