Closed18

NestJS触ってみる

mogamoga
$ mkdir nestjs-sample
$ npx @nestjs/cli new .

でひな形作ってくれる。

mogamoga

ひながたを見てると、controller, module, serviceがあって、controller.specもある。テストテンプレートまで生成してくれるのはありがたい。

mogamoga

/usersを作ってみる。
modulesが依存関係を管理してくれるみたいなのでmodulesから生成するほうがいい気がする。いきなりcontrollerから生成するとapp.modules.tsにUsersControllerが打ち込まれてしまう。

aliasがあるのでそれも活用する。
https://docs.nestjs.com/cli/usages#nest-generate

$ npx nest g mo users
$ npx nest g co users
$ npx nest g s users

するとsrc/users以下に色々作られる。

mogamoga

/users/:userID/posts みたいなのをやってみる。

$ npx nest g mo users/posts
$ npx nest g co users/posts

これでsrc/usrs/posts以下にファイルが生成される。あとはpostsControllerで以下のように書けばいけた。

import { Controller, Get, Param } from '@nestjs/common';

@Controller('users/:userID/posts')
export class PostsController {
  @Get()
  findAll(@Param('userID') userID: string) {
    return userID;
  }
}

対応コミット: https://github.com/mogaming217/nestjs-sample/commit/0292db28dda25f470cbe620ebfda2886c429ed2b

mogamoga

次は認証のガードをしてみる。ID Token持ってる人じゃないとPOSTできないみたいなやつ。公式のドキュメント読んでても、jwtをverifyした結果得られるuserID(sub)をどうControllerにわたすかよくわからなかった。

ググって見つけたこれが参考になりそう。
https://wp-kyoto.net/nestjs-auth-api-by-cognito-userpools/

やってることはGuard時に使ったServiceのメンバとしてuserIDを持ってた。なんかちょっと怖いけど、1リクエストごとにServiceのインスタンスが1つ使われる感じなら問題ない(ほんとか?)。

あとはAuthGuardみたいなのを設定すればAuthServiceのメンバに入っているはずみたいなコード書いてるところが離れてる感じなのも暗黙的でちょっと嫌だなぁ。

とりあえず簡単に実装してみる。

mogamoga

リクエストごとにServiceのインスタンスが1つ使われる感じなら問題ない

ダメだった。

  • Authorizationヘッダーあれば、AuthenticationServiceのメンバーにセットする
  • Authorizationヘッダーに適当な文字列つけてリクエストするとメンバー取れる -> OK
  • Authroziationヘッダーを空にしてリクエストしても前回のリクエスト時に設定されたメンバが取れてしまった

なんかやりかたミスってそうだな
https://github.com/mogaming217/nestjs-sample/commit/cf73b59fa4ddabd39fa642cdd5795c0a8ef34ac3

mogamoga

JWTで認証認可するときのJWT検証結果の引き回しは

  • Guard内でJWT検証して引き回したい情報に加工して req.authInfo (authInfoでもuserでも何でもいい)に突っ込む
    • 要するにHTTP Requestごとのグローバル変数みたいな感じ
    • 型情報は死ぬ(any)
  • req.authInfoを引っこ抜くCustomDecoratorを用意する
  • Controller側でCustomDecorator経由で引き回してきた情報を取る

になっちゃうかな〜まあ仕方ないかな。とりあえず認証認可周りはこれでOKとしよう。次はRDBとのつなぎこみ〜PrismaなのかTypeORMなのか。

mogamoga
$ npm i -D prisma
$ npx prisma init

でprismaディレクトリと.envが生成された。とりあえずPostgresを立てる。

version: "3"
services:
  db:
    image: postgres:10
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
      POSTGRES_DB: nestjssample
    ports:
      - 15432:5432

これで docker-compose up して使うことにしよう。prismaからこのDBにcreate dbとかschema設定とか投げたい。

mogamoga

docker-compose.yamlを微修正して初期化時にDB作られるようにする。

version: "3"
services:
  db:
    image: postgres:10
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
      POSTGRES_DB: nestjs-sample
    ports:
      - 15432:5432
    volumes:
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
CREATE DATABASE nestjssample;

Tableを作りたいのでschema.prismaを以下のようにする。Userを追記しただけ。書き方は👇を参考にする。
https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id  Int @default(autoincrement()) @id
  name  String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt @default(now())
}

こっからマイグレーションSQL発行したいので下記コマンドを叩く。previewなんですね。

$ npx prisma migrate dev --name init --preview-feature

すると ./migrations 以下にマイグレーションファイルが作成されつつApplyされる。

-- CreateTable
CREATE TABLE "User" (
    "id" SERIAL NOT NULL,
    "name" TEXT,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY ("id")
);

ちなみに本番に上げたいときは👇をやればmigrationsの中の適用されていないやつが適用されるのだろう。

$ npx prisma migrate deploy --preview-feature
mogamoga

いよいよAPIからPrisma使ってDBアクセスする。

$  npm i @prisma/client

npm iするとprisma generateで自動的にクライアントコードが生成されるらしい。schemaいじったあとは npx prisma generate たたけとのこと。
https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch-typescript-postgres#install-and-generate-prisma-client

あとはコード書くだけ。

const prisma = new PrismaClient();
const user = await prisma.user.create({
      data: {
        name: 'moga',
      },
});

これでuser作れる。

mogamoga

get, create, updateあたりは作ってみた
https://github.com/mogaming217/nestjs-sample/commit/1264f542450bf940bf3cc4afc305e1c5ddd8da8b

今はPrismaから取れるデータをそのままリターンしてるけどできればデータ変換して返すべきやつだけ返すようにしたほうがいいかな。PrismaのClientコードにUserモデルありそうだしそれ受け取って返すべきやつだけ返す処理をcontroller側で挟めばOKな気がする。

migration周りがpreviewなのは気になるけど結構サクサク作れそうだし、型による補完がめっちゃ効くからとてもコード書く分にはとてもいい感じ。

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