ゆずめも

メモ的なブログです。主に勉強した事について書いてます。

nestjsでpostのraw bodyを取得する方法

webhookのverificationでbodyParseされてないraw bodyが必要になって割と困ったのでメモ

tl;dr

結局方法3で実装した

ダメだったやり方

検索すると一番最初に以下のstack overflowが引っかかって、raw-bodyというnpmモジュールをインストールしてrawbodyを渡すデコレータ作って簡単にできるのかと思ったが、実際に動かしてみるとif(req.readable)が常にfalseになっていてうまく動作しなかった

stackoverflow.com

方法1

別のstack overflowの質問*1で見つけたやり方

nest.jsが内部で設定しているbodyParserを無効化して、Requestを拡張してrawBodyプロパティを作り、手動でbodyParserを有効にする。

自分の用途だとwebhookの時だけrawが欲しいだけなのに、毎回rawを生成するのはちょっと違和感があるけど動いた

const app = await NestFactory.create(AppModule, {
  bodyParser: false
});

const rawBodyBuffer = (req: http.IncomingMessage, res: http.ServerResponse, buf: Buffer, encoding: string) => {
  if (buf && buf.length) {
    // ここうまく型を解決する方法がわからなかった
    (req as any).rawBody = buf.toString((encoding || 'utf8') as BufferEncoding);
  }
};

app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true }));
app.use(bodyParser.json({ verify: rawBodyBuffer }));

方法2

いまいちnest.jsの挙動を理解してなくて、方法3の方が簡単だったので実際には試してない。
jsonパース用のmiddlewareとbodyをrawで提供するmiddlewareを作成して、app.module.tsで設定するやり方

stackoverflow.com

AppModuleというかNestModuleにはconfigureという関数があって、apply, forRoutesを組み合わせて特定のmiddlewareを適用することができるの初めて知った

docs.nestjs.com

方法3

シンプルにapp.useを使うやり方
簡単だったし、どこかで特定のパスを指定しないといけないならmain.tsで指定してやるのが大体の設定が書かれているのでいい気がした

 app.use('/webhook', express.raw({type: 'application/json'}));

終わりに

記事に直接関係無いけど、今回調べててexpressにbody-parserが入ってわざわざ別でbody-parserインストールする必要がなくなってたことを知った

nest.jsで特定のパスだけcsrufのチェックを回避する

nest.jsで開発をしていて、webhookを利用したい時に特定のパスだけcsurfチェックを外したい時に公式の説明とかだと「csrfのガードの前に特定のエンドポイントを設定する」方法しか書かれていなくてnestでは使えなかった。
そのため直接app.use(csurf())するのではなく、ひと手間加えることで特定のパスを例外処理することができた

以下issueで紹介されていた方法をTypeScriptで型付し、optionを設定できるようにしたものです

import * as csurf from 'csurf'
import {RequestHandler} from 'express'

export function specificCsurf(
  ignoreUrls: RegExp,
  options?: Parameters<typeof csurf>[0]
): RequestHandler {
  const csrf = csurf(options)
  return (req, res, next) => {
    if (ignoreUrls.test(req.path) {
      return next()
    }
    return csrf(req, res, next)
  }
}

// app.ts
// 色々省略
app.use(/^/(special|nocsrf)/, {/* csurf options */})

参考

github.com

tsでクラスからファンクション以外のプロパティを抜き出した型を作る

Typescriptでコードを書いていてクラスの型から関数以外を抜き出した型が欲しくて、typescript meetupでMapped Typesとかのセッション見て頑張ったらできるのでは🤔ということでやってみた

typescript-jp.connpass.com

要するに以下のようなクラスからxyだけの型が欲しい

class Hoge {
  x: string
  y: number

  constructor(partial: Partial<Hoge>) {
    Object.assign(this, partial)
  }

  getHoge(): string {
    return `${this.x}-${this.y}`
  }
}

乃木坂46のANN聞きながら作ったやつ

export type OmitFunction<T> = Pick<
  T,
  {[K in keyof T]: T[K] extends (...args: any) => any ? never : K}[keyof T]
>

これを使うとclassの型からpropertyだけ取り出せる

type A = OmitFunction<Hoge> // => A is {x: string, y: number}

参考

ちなみに

ググったけど出てこなかったから1時間くらい悩んでやっと解決したのに、自分が付けた型名から検索ワードを考えた結果stackoverflowにすでにあった。。。

このへんどういうキーワード入れてやればいいかわからないのと、もうちょっといい感じの書き方ないのか?ってのが常についてまわる

stackoverflow.com

ただ回答見ても、同じようなコードだしなんかもっといい方法無いんだろうか