NestJS
Node.jsとは?
Node.jsはJavaScriptをブラウザ外で実行するためのプラットフォーム。
プラットフォームとフレームワークって違うのか?
- プラットフォーム
- 実行環境を提供するもの
- Node.jsはjsがブラウザ以外の環境(特にサーバーサイド)で動作することを可能にする
- 実行環境とはOSやDockerのようなもの←しっくりくる
- フレームワーク
- 開発の枠組みを提供するもの
- 特定の構造やデザインパターンに従うことをサポートする
- NestJSはNode.js上で動作するフレームワークである
コメント
node.jsのことが分かっていなかったが、この「OSやDockerのような実行環境を提供する」という説明でしっくりきた。
TypeScriptとは?
jsのスーパーセットである。
スーパーセットとは?
ある集合が別の集合の全てを含む上に、さらに追加の要素を持っている状態。
つまり、TypeScriptはjsの全ての機能を含み、さらに追加機能を持っている。
jsはTSでも動作するが、TSはjsでは動作しない。
TSは静的チェック、クラスベースのOOPを提供する。
jQueryのようなもの?
異なる。
jQueryはライブラリの1種であり、新しい言語機能を追加するのではなく、
各種操作を簡単に行うための機能を提供するもの。
コメント
当たり前だが、「スーパーセット」「ライブラリ」のような、単語ごとに意味が異なるので、それらを理解していないと違いが分からなくなる。
他にスーパーセットになる言語がないか調べたところ、pythonのスーパーセットとなる「Mojo」なるものがあるらしい。
Angularとは
フロントエンドのフレームワーク。
DIやモジュールなどの概念がNestにも採用されている。
Angular開発者はNestでのサーバーサイド開発にも容易に慣れることができる。
ReactやVue
これらもフロントエンドのフレームワーク。
Nestとの直接的な関係は無いが、Nestと組み合わせて使うことができる。
例)Nestでバック、ReactやVueでフロントを構築する
(余談)Nuxt.jsやNext.jsって何?
それぞれVue.jsとReact.jsに基づくフレームワーク。
サーバーサイドレンダリングや静的サイト生成に重点を置いている。
VueやReactもフレームワークでは?フレームワークのフレームワークってこと?
一部正確。
Vue、ReactはUI構築のための基本的なツールを提供する。
NuxtやNextはこれらを使い、より大規模・複雑なアプリを効率的に開発できるようにする機能を提供する。
さらに言うと、Vue.jsはフレームワークであり、React.jsはライブラリである。
コメント
ここら辺が「フロントエンドって何かややこしそうだな...」となってしまった元凶だったと思い出した。「N~.js」とつくものが多すぎる&別物であると言う点。
今回はnuxtやnextは触らないが、いずれここら辺もしっかり整理したい。
Vue.jsとReact.jsは何?
Vue.js
フレームワーク。データバインディングやコンポーネントベースのアーキテクチャが特徴。
React.js
UIライブラリ。コンポーネントベース、仮装DOMが特徴。
→違うもの?
全体か一部か
フレームワークは、アプリケーションの設計や構造など全体的な枠組みを提供。
UIライブラリは、UIの構築に特化した機能を提供する。他範囲の定義はしない。
→要件、好み、プロジェクトのスケールで選択すれば良い
コメント
横並びのものだと思っていたが、実際はかなり違った。
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で作成されたプロジェクトは「モジュール毎に専用のディレクトリを持つ」という慣例に従うように促すプロジェクト構造を意識している。
Expressとfastify
Nestでは任意のHTTPフレームワークと連携可能であり、
「Express」と「fastify」の2つのプラットフォームがすぐに使用できる。
HTTPフレームワークとは?
HTTP通信を扱うためのサーバー側アプリケーションの構築をサポートするフレームワーク。
リクエストの受信、処理、レスポンスの送信などのwebサーバーとしての機能を提供する。
どちらが使われるかは気にしなくて良い
どのプラットフォームであってもNestのアプリケーションインターフェイスが公開される。
チュートリアルをやってみる
公式のチュートリアルは有償(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
コントローラー生成
npx nest g controller todo
g generateの略
controller 生成する対象
todo 生成名
実行後、src配下にtodoディレクトリが生成され、controllerとtestのファイルが生成される。
DB接続
Prismaを使う。
Prismaのインストール&初期設定を行う
npm i prisma
npx prisma init
生成されるファイルについて
- .env:接続情報を記載。情報はDockerfileにあるため参照
- prisma/schema.prisma:テーブル定義を記述
PrismaClientクラス
各テーブルへのアクセスの際に使う。
Nestから使用する場合は、このクラスの拡張サービスから利用する。
テーブル生成
-
prisma/schema.prisma
にテーブル定義を記述 -
migrateコマンド実行
npx prisma migrate dev --name todo dev:テーブル生成と共に、migrateファイルを作成する。prisma/migrationsディレクトリ配下に生成。 --name:マイグレーション名。マイグレーションの目的を伝える。
接続 psql -h <ホスト名> -p <ポート番号> -d <データベース名> -U <ユーザー名> データベースlist \list テーブルlist \dt 構造表示 \d tablename
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.