NestJS触ってみる
仕事で使うかもしれないので触る。あとでORM(TypeORM?, Prisma?)も触って見るかも。blitzでもいいのかもしれないが不安定という話も聞くのでとりあえずnest。
このリポジトリで触っていく。
目標
- NestJSの感じ掴む
- ORMの選定(Prisma or TypeORM)
$ mkdir nestjs-sample
$ npx @nestjs/cli new .
でひな形作ってくれる。
ひながたを見てると、controller, module, serviceがあって、controller.specもある。テストテンプレートまで生成してくれるのはありがたい。
/usersを作ってみる。
modulesが依存関係を管理してくれるみたいなのでmodulesから生成するほうがいい気がする。いきなりcontrollerから生成するとapp.modules.tsにUsersControllerが打ち込まれてしまう。
aliasがあるのでそれも活用する。
$ npx nest g mo users
$ npx nest g co users
$ npx nest g s users
するとsrc/users以下に色々作られる。
/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
次は認証のガードをしてみる。ID Token持ってる人じゃないとPOSTできないみたいなやつ。公式のドキュメント読んでても、jwtをverifyした結果得られるuserID(sub)をどうControllerにわたすかよくわからなかった。
ググって見つけたこれが参考になりそう。
やってることはGuard時に使ったServiceのメンバとしてuserIDを持ってた。なんかちょっと怖いけど、1リクエストごとにServiceのインスタンスが1つ使われる感じなら問題ない(ほんとか?)。
あとはAuthGuardみたいなのを設定すればAuthServiceのメンバに入っているはずみたいなコード書いてるところが離れてる感じなのも暗黙的でちょっと嫌だなぁ。
とりあえず簡単に実装してみる。
リクエストごとにServiceのインスタンスが1つ使われる感じなら問題ない
ダメだった。
- Authorizationヘッダーあれば、AuthenticationServiceのメンバーにセットする
- Authorizationヘッダーに適当な文字列つけてリクエストするとメンバー取れる -> OK
- Authroziationヘッダーを空にしてリクエストしても前回のリクエスト時に設定されたメンバが取れてしまった
なんかやりかたミスってそうだな
JWTで認証認可するときのJWT検証結果の引き回しは
- Guard内でJWT検証して引き回したい情報に加工して
req.authInfo
(authInfoでもuserでも何でもいい)に突っ込む- 要するにHTTP Requestごとのグローバル変数みたいな感じ
- 型情報は死ぬ(any)
- req.authInfoを引っこ抜くCustomDecoratorを用意する
- Controller側でCustomDecorator経由で引き回してきた情報を取る
になっちゃうかな〜まあ仕方ないかな。とりあえず認証認可周りはこれでOKとしよう。次はRDBとのつなぎこみ〜PrismaなのかTypeORMなのか。
Prisma側の記事だし偏ってそうだけど読むか
読んでるけど後発だけあってTypeSafe度合いがぜんぜん違うみたいだな〜Prisma確かに魅力的ではある。DB周りで困りそうなのはMigrationなのでそこだけが心配かな…。
catnoseさんに教えてもらったやつ。週明け見る。
とりあえずPrismaをNestJSに突っ込んで触ってみよう!RECIPEがあったのでココを読む
$ 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設定とか投げたい。
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を追記しただけ。書き方は👇を参考にする。
// 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
いよいよAPIからPrisma使ってDBアクセスする。
$ npm i @prisma/client
npm iするとprisma generate
で自動的にクライアントコードが生成されるらしい。schemaいじったあとは npx prisma generate
たたけとのこと。
あとはコード書くだけ。
const prisma = new PrismaClient();
const user = await prisma.user.create({
data: {
name: 'moga',
},
});
これでuser作れる。
get, create, updateあたりは作ってみた
今はPrismaから取れるデータをそのままリターンしてるけどできればデータ変換して返すべきやつだけ返すようにしたほうがいいかな。PrismaのClientコードにUserモデルありそうだしそれ受け取って返すべきやつだけ返す処理をcontroller側で挟めばOKな気がする。
migration周りがpreviewなのは気になるけど結構サクサク作れそうだし、型による補完がめっちゃ効くからとてもコード書く分にはとてもいい感じ。