Closed3

NestJS+Angular+Scullyを試してみる

水無瀬水無瀬

セットアップ

nxを使ってNestJS+Angularまではやる。

$ npx create-nx-workspace
npx: installed 190 in 24.11s
? Workspace name (e.g., org name)     nx-scully-sample
? What to create in the new workspace angular-nest      [a workspace with a full stack application (Angular + Nest)]
? Application name                    nx-scully-sample
? Default stylesheet format           SASS(.scss)  [ http://sass-lang.com   ]
? Default linter                      ESLint [ Modern linting tool ]
? Use Nx Cloud? (It's free and doesn't require registration.) No

Scully追加

$ npm run ng add @scullyio/init -- --nx-scully-sample

RouterModule追加

ScullyがAngularRouterが要求するので追加する。
これがないとScullyでビルドした結果にアクセスするときにNullInjectorで落ちる。
(ハマって数時間溶かした)

RouterModuleを追加するために雑にコンポーネントを作って対応する。

top.component

nxが生成したapp.componentに記載されてる内容を切り出していく。
※cssについては割愛

top.component.html
<div style="text-align: center">
  <h1>Welcome to nx-scully-sample!</h1>
  <img
    width="450"
    src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png"
  />
</div>
<div>Message: {{ hello$ | async | json }}</div>
top.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Message } from '@nx-scully-sample/api-interfaces';

@Component({
  selector: 'nx-scully-sample-root',
  templateUrl: './top.component.html',
  styleUrls: ['./top.component.scss'],
})
export class TopComponent {
  hello$ = this.http.get<Message>('/api/hello');
  constructor(private http: HttpClient) {}
}

app-router.module.tsを追加

app-router.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TopComponent } from './components/top/top.component';

const routes: Routes = [
  { path: '', component: TopComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.module.ts修正

RouterModuleと作ったComponentを読み込む様に修正。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { ScullyLibModule } from '@scullyio/ng-lib';
import { AppRoutingModule } from './app-router.module';
import { TopComponent } from './components/top/top.component';

@NgModule({
  declarations: [AppComponent, TopComponent],
  imports: [BrowserModule, HttpClientModule, ScullyLibModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

app.component.html

routingに対応する様に修正する。

app.component.html
<router-outlet></router-outlet>

ビルド

# Angularのビルド
$ npm run build --  --prod
# scullyのビルド
$ npm run scully -- --scanRoutes

起動

これで起動できればOK。

$ npm run scully:serve
水無瀬水無瀬

firebaseにあげてみる

準備

$ npx firebase init
> functionsとhostingを選択
> 適当にproject選択(今回はあるもの選んだ)
> 言語選択はお好みで(今回はビルドしたもの上げるのでjs)
> eslintの質問はNo
> npm installもNo
> spaじゃなくなっているので全URLを/index.htmlに飛ばすのはNo
> 404の上書きもNo
> index.htmlの上書きもNo

firebase.jsonの修正

{
  "hosting": {
    "public": "dist/static",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/v1/**/*",
        "function": "api"
      },
      {
        "source": "/*[!v1]*/**",
        "destination": "/"
      }
    ]
  },
  "functions": {
    "source": "dist/apps/api"
  }
}

apps/api/main.tsの修正

デフォのままじゃfirebase functionsで動かないので修正。

import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';

import { AppModule } from './app/app.module';
import * as express from 'express';
import * as functions from 'firebase-functions';

const server = express();

async function bootstrap(instance) {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(instance));
  app.setGlobalPrefix('v1');
  return app.init();
}

bootstrap(server)
  .then(v => Logger.log(`Ready`))
  .catch(e => Logger.warn(e));
export const api = functions.https.onRequest(server);

top.component.ts修正

APIのパス変えたので修正する。
prefixを変えるのみ。

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Message } from '@nx-scully-sample/api-interfaces';

@Component({
  selector: 'nx-scully-sample-root',
  templateUrl: './top.component.html',
  styleUrls: ['./top.component.scss'],
})
export class TopComponent {
  hello$ = this.http.get<Message>('/v1/hello');
  constructor(private http: HttpClient) {}
}

デプロイ先にpackage.jsonを追加

firebase functions用にAPIのデプロイ先にpackage.jsonを追加するscriptを用意する。
/tools/generator配下にpackage-json-generator.jsを作成

const path = require('path');
const fs = require('fs');

const original = require('../../package.json');
const ROOT_PATH = path.resolve(__dirname, '../../');
const OUTPUT_PATH = path.resolve(ROOT_PATH, 'dist/apps/api');

const functionsJson = {
  main: "main.js",
  engines: {
    node: "12"
  }
};
(function writeJson() {
  const dependencies = original.dependencies;
  const writeJson = {
    ...functionsJson,
    dependencies
  }
  fs.writeFileSync(`${OUTPUT_PATH}/package.json`, JSON.stringify(writeJson));
})();
水無瀬水無瀬

ビルド & デプロイ

script準備

package.jsonに↓を追加 or 修正

{
  "scriptis": {
    "scully": "scully",
    "scully:prod": "scully --scanRoutes --prod --RSD",
    "firebase": "firebase",
    "firebase:deploy": "firebase deploy",
    "generate:package": "node tools/generators/package-json-generator.js"
  },
}

上2つがscully用であとはfirebase周り用。

# nx周りをきれいに設定すればaffected:buildで完結する気がする
# APIのビルド
$ npm run affected:build

# Scully用にAngularのビルド
$ npm run build -- --prod

# Scullyのビルド
$ npm run scully:prod

# デプロイ
$ npm run firebase:deploy

デプロイ後、hostingのURLでアプリが表示されていればOK。

このスクラップは2021/01/21にクローズされました