Open12

NestJS

TakafumiTakafumi

Node.jsとは?

Node.jsはJavaScriptをブラウザ外で実行するためのプラットフォーム。

プラットフォームとフレームワークって違うのか?

  • プラットフォーム
    • 実行環境を提供するもの
    • Node.jsはjsがブラウザ以外の環境(特にサーバーサイド)で動作することを可能にする
    • 実行環境とはOSやDockerのようなもの←しっくりくる
  • フレームワーク
    • 開発の枠組みを提供するもの
    • 特定の構造やデザインパターンに従うことをサポートする
    • NestJSはNode.js上で動作するフレームワークである

コメント

node.jsのことが分かっていなかったが、この「OSやDockerのような実行環境を提供する」という説明でしっくりきた。

TakafumiTakafumi

TypeScriptとは?

jsのスーパーセットである。

スーパーセットとは?

ある集合が別の集合の全てを含む上に、さらに追加の要素を持っている状態。
つまり、TypeScriptはjsの全ての機能を含み、さらに追加機能を持っている
jsはTSでも動作するが、TSはjsでは動作しない。
TSは静的チェック、クラスベースのOOPを提供する。

jQueryのようなもの?

異なる。
jQueryはライブラリの1種であり、新しい言語機能を追加するのではなく、
各種操作を簡単に行うための機能を提供するもの。

コメント

当たり前だが、「スーパーセット」「ライブラリ」のような、単語ごとに意味が異なるので、それらを理解していないと違いが分からなくなる。
他にスーパーセットになる言語がないか調べたところ、pythonのスーパーセットとなる「Mojo」なるものがあるらしい。
https://codezine.jp/article/detail/18322

TakafumiTakafumi

Angularとは

フロントエンドのフレームワーク。
DIやモジュールなどの概念がNestにも採用されている。
Angular開発者はNestでのサーバーサイド開発にも容易に慣れることができる。

ReactやVue

これらもフロントエンドのフレームワーク。
Nestとの直接的な関係は無いが、Nestと組み合わせて使うことができる。
例)Nestでバック、ReactやVueでフロントを構築する

TakafumiTakafumi

(余談)Nuxt.jsやNext.jsって何?

それぞれVue.jsとReact.jsに基づくフレームワーク。
サーバーサイドレンダリングや静的サイト生成に重点を置いている。

VueやReactもフレームワークでは?フレームワークのフレームワークってこと?

一部正確。
Vue、ReactはUI構築のための基本的なツールを提供する。
NuxtやNextはこれらを使い、より大規模・複雑なアプリを効率的に開発できるようにする機能を提供する。

さらに言うと、Vue.jsはフレームワークであり、React.jsはライブラリである。

コメント

ここら辺が「フロントエンドって何かややこしそうだな...」となってしまった元凶だったと思い出した。「N~.js」とつくものが多すぎる&別物であると言う点。
今回はnuxtやnextは触らないが、いずれここら辺もしっかり整理したい。

TakafumiTakafumi

Vue.jsとReact.jsは何?

Vue.js
フレームワーク。データバインディングやコンポーネントベースのアーキテクチャが特徴。

React.js
UIライブラリ。コンポーネントベース、仮装DOMが特徴。

→違うもの?

全体か一部か

フレームワークは、アプリケーションの設計や構造など全体的な枠組みを提供。
UIライブラリは、UIの構築に特化した機能を提供する。他範囲の定義はしない。
要件、好み、プロジェクトのスケールで選択すれば良い

コメント

横並びのものだと思っていたが、実際はかなり違った。

TakafumiTakafumi

NestJs環境をつくる

dockerの公式imageをpull/build/run。npmでnestjs/cliをインストール。
nestcliが使えるようになるので、nest new ~ でプロジェクトを作成

$ docker pull node
$ docker run -itd node
$ npm i -g @nestjs/cli
$ nest new project-name

main.ts

main.tsでアプリケーションをブートストラップ(アプリが動く状態になるまでの一連の処理)する。
アプリケーションインスタンスの生成にはNestFactoryクラスを使っている。

nestcliで作成されたプロジェクトは「モジュール毎に専用のディレクトリを持つ」という慣例に従うように促すプロジェクト構造を意識している。

TakafumiTakafumi

Expressとfastify

Nestでは任意のHTTPフレームワークと連携可能であり、
「Express」と「fastify」の2つのプラットフォームがすぐに使用できる。

HTTPフレームワークとは?

HTTP通信を扱うためのサーバー側アプリケーションの構築をサポートするフレームワーク。
リクエストの受信、処理、レスポンスの送信などのwebサーバーとしての機能を提供する。

どちらが使われるかは気にしなくて良い

どのプラットフォームであってもNestのアプリケーションインターフェイスが公開される。

TakafumiTakafumi

チュートリアルをやってみる

公式のチュートリアルは有償(1万ちょっと)だったため、Zennにあったものをやってみる

環境構築

チュートリアルをやるためにnestJs環境を構築する。
以下の環境が必要。docker上で構築する。

  • node.js
  • postgres

Dockerfileとdocker-compose.ymlが以下。
ただし、ローカルにnestのプロジェクトが存在しないと「volumes: - ./app:/app」の部分でエラーになる。
以下どちらかの対応が必要。

  • docker-compose.yml内のvolume部分を一旦コメントアウトして環境構築
    • コンテナ内のapp配下のディレクトリをコンテナにマウントする
    • 手軽でローカルマシンが汚れない
    • ただ、Dockerfile内で行なっているnpm ~の実行は無意味になる(ローカルのappで上書きされる)
  • ローカル環境でnestプロジェクトを作成し、それをコンテナにマウントする
    • ローカルにnode.jsやnestjs/cliのインストールが必要なため、ローカルが汚れる
    • どちらにしろDockerfile内のnpm ~が無意味
      今回は前者で対応済み。

docker-compose.yml

version: '3.8'

services:
  node:
    build:
      context: ./infra/node/
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:password@postgresdb/db
    volumes:
      - ./app:/app
    command: nest start --watch

  postgresdb:
    build:
      context: ./infra/postgresdb/
    ports:
      - "5432:5432"

/infra/node/Dockerfile

FROM node:16

RUN npm init -y
RUN npm install -g @nestjs/cli
RUN npx nest new app --package-manager=npm

WORKDIR /app

EXPOSE 3000

/infra/postgresdb/Dockerfile

FROM postgres:latest

ENV POSTGRES_DB=db
ENV POSTGRES_USER=user
ENV POSTGRES_PASSWORD=password

EXPOSE 5432
TakafumiTakafumi

コントローラー生成

npx nest g controller todo
g generateの略
controller 生成する対象
todo 生成名

実行後、src配下にtodoディレクトリが生成され、controllerとtestのファイルが生成される。

TakafumiTakafumi

DB接続

Prismaを使う。
Prismaのインストール&初期設定を行う

npm i prisma
npx prisma init

生成されるファイルについて

  • .env:接続情報を記載。情報はDockerfileにあるため参照
  • prisma/schema.prisma:テーブル定義を記述

PrismaClientクラス

各テーブルへのアクセスの際に使う。
Nestから使用する場合は、このクラスの拡張サービスから利用する。

TakafumiTakafumi

テーブル生成

  1. prisma/schema.prismaにテーブル定義を記述

  2. migrateコマンド実行

    npx prisma migrate dev --name todo
    dev:テーブル生成と共に、migrateファイルを作成する。prisma/migrationsディレクトリ配下に生成。
    --name:マイグレーション名。マイグレーションの目的を伝える。
    

    postgres確認

    接続
    psql -h <ホスト名> -p <ポート番号> -d <データベース名> -U <ユーザー名>
    
    データベースlist
    \list
    
    テーブルlist
    \dt
    
    構造表示
    \d tablename
    
TakafumiTakafumi

DBにデータを入れた後、curlでAPIを叩いたがレスポンスにテストデータが含まれていない。

状態

  • DBにはデータが入っている
  • コンテナ内のソースにもDB接続の変更が反映されている
  • 変更前のデータがレスポンスされている

コンテナの再起動後、nodeコンテナサーバーにアクセスが出来なくなっていた。
nodeコンテナに入り、nest start --watchを実行。以下のエラーが発生。

[5:51:12 AM] Starting compilation in watch mode...

src/prisma/prisma.service.ts:11:14 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.

11     this.$on('beforeExit', async () => {
                ~~~~~~~~~~~~

[5:51:24 AM] Found 1 error. Watching for file changes.

$onに渡していた型が対応していなかったことでエラーになっていた。
エラーになっていたことでコンパイルが動いておらず、古いコードの状態で動作していた。
以下のように修正。

修正前
  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }

修正後
  async onApplicationShutdown(signal: string) {
    await this.$disconnect();
  }

修正後、コンテナ内で動かしていたwatchでエラーが解消されたことを確認。

[5:56:56 AM] File change detected. Starting incremental compilation...
[5:56:57 AM] Found 0 errors. Watching for file changes.

コード変更時のログはdockerのログで確認が可能。

エラーが起きたコードに戻してdockerログを見たところ、同じエラーログが出ていた。
今後はここを見ながら開発すると良さそう。

// dockerコンテナのログ
docker compose logs -f

// エラーログ
[6:24:21 AM] File change detected. Starting incremental compilation...
node_1        | 
node_1        | src/prisma/prisma.service.ts:11:14 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
node_1        | 
node_1        | 11     this.$on('beforeExit', async () => {
node_1        |                 ~~~~~~~~~~~~
node_1        | 
node_1        | [6:24:22 AM] Found 1 error. Watching for file changes.