Node.js
Express

express-validator新旧API比較

はじめに

Expressを調べる機会があり、バリデーションということでexpress-validatorも調べようとしたのですが、
・・・あるぇ?本とかに書いてあるのとAPIが全然違う?(。´・ω・)?

どうやら2017年8月末にリリースされたv4.0.0から新しいAPIが導入されたようです。誰も何も言ってないのはlegacy APIとして従来のAPIも提供されているからでしょう。

比較してみた

新旧のAPIで同じことをするコードを書いてみました。プログラム全体は以下にあります。
https://github.com/junjis0203/comparison_express-validator_api

以下、APIを見比べて感じたことです。

あるパラメータに対するvalidateとsanitizeがまとめて書けるようになった

これが大きい気がします。旧APIでは以下のようにcheckとsanitizeを分けて書かないといけませんでした。また、sanitizeはチェーンではなくサニタイズされた結果を返すためサンプルのように分けて書く必要があり非常にいけてません。

legacy_api.js抜粋
    req.check('name', 'Name must be specified').notEmpty();
    req.check('mail', "E-mail don't look like E-mail").isEmail();

    // legacy API can't chain sanitization
    req.sanitize('mail').trim();
    req.sanitize('mail').normalizeEmail();

新APIではこんな感じに書けます。ふとどれがvalidationでどれがsanitizationかわかりにくいかもという気もしましたがこっちの方がかっこいいからよし(個人の感想です)

new_api.js抜粋
    check('mail', "E-mail don't look like E-mail")
        .isEmail()
        .trim()
        .normalizeEmail()

custom validatorが書きやすくなった

今度は新APIを先に見せます。先のmailパラメータに対するvalidationには続きがあって以下のようにcustomメソッドですでに登録されているメールアドレスかチェックしています。
ちなみにJavascript素人でPromiseよくわからないのでexpress-validatorのUsageとは違いベタにtrue返してます。(true返さないとvalidation failになるのがわかりにくかった)

new_api.js抜粋
    check('mail', "E-mail don't look like E-mail")
        .isEmail()
        .trim()
        .normalizeEmail()
        .custom(function(value) {
            if (exists_entry(value)) {
                throw new Error('this email is already in use');
            }
            return true;
        })

旧APIにもcustom validatorを登録する方法はあるのですが、なんとなくこういうアプリ固有チェック的なものはグローバルなvalidatorとして登録するのはいまいちなんじゃないか(個人の感想です)と思い旧API使用サンプルではValidationResult取得後に確認を行っています。

legacy_api.js抜粋
    req.getValidationResult().then(function(result) {
        errors = undefined;
        if (!result.isEmpty()) {
            errors = result.array();
        } else {
            entry = {name: req.body.name, mail: req.body.mail};
            // this check is too specific for installing custom validator
            if (exists_entry(entry.mail)) {
                errors = [{ param: 'mail', msg: 'this E-mail address is already in use' }];
            } else {
                add_entry(entry);
            }
        }

validationしたパラメータだけをまとめたオブジェクトを返すAPIができた

旧APIでは上のコードにあるようにvalidationを行った後、reqオブジェクトから改めて値を取り出す必要があります。超いけてません(個人の感想です)

新APIではvalidation(sanitiationのみのものも可)したパラメータをまとめてオブジェクトとして返すmatchedDataが提供されています。

new_api.js抜粋
        entry = matchedData(req);

どこから取り出さないといけないかが隠蔽されてていい雰囲気ですね。
そういえば書き忘れていましたが新APIは処理本体の前にミドルウェア的?に記述するようです。パラメータが満たすべき条件を宣言的に書けるのがいいなと思いました。

新APIのここがダメ

notEmptyが提供されていません。
はじめexistsがそれだろうと使っても空文字列がすり抜けるので確認したところ「existsはundefinedじゃなければおk」とのことでした。

notEmpty相当のことをするには、isLength({min: 1})とする必要があります。

(追記)
not().isEmpty()と書けばよいとのコメントをいただきました。

まとめ

express-validatorの新APIでいいなと思った点をまとめます。

  • 旧APIでは分かれていたvalidateとsanitizeをパラメータごとに書けるようになった
  • アプリ固有チェック的custom validationが書きやすい
  • 宣言と処理が分離されており、パラメータをまとめたオブジェクトが取得できる