ノードAPIスキーマ検証にJoiを使用する方法

提供:Dev Guides
移動先:案内検索

序章

新しいユーザーを作成するためにAPIエンドポイントで作業していると想像してください。 firstnamelastnameagebirthdateなどのユーザーデータをリクエストに含める必要があります。 数値を期待しているときに、ユーザーが誤ってageフィールドの値として自分の名前を入力することは望ましくありません。 特定の日付形式を期待しているときに、birthdateフィールドに誕生日を入力するユーザーも望ましくありません。 悪いデータがアプリケーションを通過することは望ましくありません。 これは、データ検証で対処できます。

Sequelize、Knex、Mongoose(MongoDBの場合)などのノードアプリケーションを構築するときに ORM(オブジェクトリレーショナルマッピング)を使用したことがある場合は、検証制約を設定できることがわかります。モデルスキーマ。 これにより、データをデータベースに永続化する前に、アプリケーションレベルでのデータの処理と検証が容易になります。 APIを構築する場合、データは通常、特定のエンドポイントへのHTTPリクエストから取得され、リクエストレベルでデータを検証できるようにする必要がすぐに発生する可能性があります。

このチュートリアルでは、Joi検証モジュールを使用してリクエストレベルでデータを検証する方法を学習します。 APIリファレンスを確認すると、Joiとサポートされているスキーマタイプの使用方法について詳しく知ることができます。

このチュートリアルを終了すると、次のことができるようになります。

  • リクエストデータパラメータの検証スキーマを作成します
  • 検証エラーを処理し、適切なフィードバックを提供する
  • リクエストをインターセプトして検証するミドルウェアを作成する

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、ノードv14.2.0、npm v6.14.5、およびjoiv13.0.2で検証されました。

ステップ1—プロジェクトの設定

このチュートリアルでは、学校のポータルを構築していて、APIエンドポイントを作成したいとします。

  • /people:新しい生徒と教師を追加します
  • /auth/edit:教師のログイン資格情報を設定します
  • /fees/pay:学生に料金を支払う

Expressを使用してこのチュートリアルのRESTAPIを作成し、Joiスキーマをテストします。

まず、コマンドラインターミナルを開き、新しいプロジェクトディレクトリを作成します。

mkdir joi-schema-validation

次に、そのディレクトリに移動します。

cd joi-schema-validation

次のコマンドを実行して、新しいプロジェクトを設定します。

npm init -y

そして、必要な依存関係をインストールします。

npm install [email protected]<6> [email protected] [email protected] [email protected] [email protected]<^>

プロジェクトのルートディレクトリにapp.jsという名前の新しいファイルを作成して、Expressアプリを設定します。

nano app.js

これがアプリケーションのスターターセットアップです。

まず、expressmorgan、およびbody-parserが必要です。

app.js

// load app dependencies
const express = require('express');
const logger = require('morgan');
const bodyParser = require('body-parser');

次に、appを初期化します。

app.js

// ...

const app = express();
const port = process.env.NODE_ENV || 3000;

// app configurations
app.set('port', port);

// establish http server connection
app.listen(port, () => { console.log(`App running on port ${port}`) });

次に、morganロギングとbody-parserミドルウェアをアプリのリクエストパイプラインに追加します。

app.js

// ...

const app = express();
const port = process.env.NODE_ENV || 3000;

// app configurations
app.set('port', port);

// load app middlewares
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// establish http server connection
app.listen(port, () => { console.log(`App running on port ${port}`) });

これらのミドルウェアは、application/jsonおよびapplication/x-www-form-urlencodedリクエストの現在のHTTPリクエストの本文をフェッチして解析し、リクエストのルート処理ミドルウェアのreq.bodyで使用できるようにします。

次に、Routesを追加します。

app.js

// ...

const Routes = require('./routes');

const app = express();
const port = process.env.NODE_ENV || 3000;

// app configurations
app.set('port', port);

// load app middlewares
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// load our API routes
app.use('/', Routes);

// establish http server connection
app.listen(port, () => { console.log(`App running on port ${port}`) });

app.jsファイルは今のところ完成しています。

エンドポイントの処理

アプリケーションのセットアップから、routes.jsファイルからルートをフェッチすることを指定しました。

プロジェクトのルートディレクトリにファイルを作成しましょう。

nano routes.js

expressを要求し、"success"の応答とリクエスト内のデータを使用してリクエストを処理します。

ルート.js

const express = require('express');
const router = express.Router();

// generic route handler
const genericHandler = (req, res, next) => {
  res.json({
    status: 'success',
    data: req.body
  });
};

module.exports = router;

次に、peopleauth/edit、およびfees/payのエンドポイントを確立します。

ルート.js

// ...

// create a new teacher or student
router.post('/people', genericHandler);

// change auth credentials for teachers
router.post('/auth/edit', genericHandler);

// accept fee payments for students
router.post('/fees/pay', genericHandler);

module.exports = router;

これで、POSTリクエストがこれらのエンドポイントのいずれかにヒットすると、アプリケーションはgenericHandlerを使用して応答を送信します。

最後に、startスクリプトをpackage.jsonファイルのscriptsセクションに追加します。

nano package.json

次のようになります。

package.json

// ...
"scripts": {
  "start": "node app.js"
},
// ...

アプリを実行して、これまでに何があり、すべてが正しく機能していることを確認します。

npm start

"App running on port 3000"のようなメッセージが表示されます。 サービスが実行されているポート番号をメモします。 そして、アプリケーションをバックグラウンドで実行したままにします。

エンドポイントのテスト

Postmanなどのアプリケーションを使用してAPIエンドポイントをテストできます。

注: Postmanを初めて使用する場合は、このチュートリアルでPostmanを使用するためのいくつかの手順を次に示します。

  • 新しいリクエストの作成から始めます。
  • リクエストタイプをPOSTに設定します(デフォルトでは、 GET に設定されている場合があります)。
  • Enter request URL フィールドに、サーバーの場所(ほとんどの場合、localhost:3000)とエンドポイント(この場合は/people)を入力します。
  • ボディを選択します。
  • エンコーディングタイプをRawに設定します(デフォルトでは、 none "に設定されている場合があります)。
  • フォーマットをJSONに設定します(デフォルトでは、 Text に設定されている場合があります)。
  • あなたのデータを入れてください。

次に、送信をクリックして応答を表示します。


管理者が「GladChinda」という名前の教師の新しいアカウントを作成しているシナリオを考えてみましょう。

このサンプルリクエストを提供しました:

{
    "type": "TEACHER",
    "firstname": "Glad",
    "lastname": "Chinda"
}

次の応答例を受け取ります。

Output{
    "status": "success",
    "data": {
        "type": "TEACHER",
        "firstname": "Glad",
        "lastname": "Chinda"
    }
}

"success"ステータスを受け取り、送信したデータが応答にキャプチャされます。 これにより、アプリケーションが期待どおりに機能していることが確認されます。

ステップ2—Joi検証ルールを試す

簡単な例は、後のステップで何を達成するかについてのアイデアを与えるのに役立つ場合があります。

この例では、Joiを使用して検証ルールを作成し、新しいユーザーを作成するリクエストの電子メール、電話番号、および誕生日を検証します。 検証が失敗した場合は、エラーを送り返します。 それ以外の場合は、ユーザーデータを返します。

testエンドポイントをapp.jsファイルに追加しましょう。

nano app.js

次のコードスニペットを追加します。

app.js

// ...

app.use('/', Routes);

app.post('/test', (req, res, next) => {
  const Joi = require('joi');

  const data = req.body;

  const schema = Joi.object().keys({
    email: Joi.string().email().required(),
    phone: Joi.string().regex(/^\d{3}-\d{3}-\d{4}$/).required(),
    birthday: Joi.date().max('1-1-2004').iso()
  });
});

// establish http server connection
app.listen(port, () => { console.log(`App running on port ${port}`) });

このコードは、新しい/testエンドポイントを追加します。 リクエスト本文からdataを定義します。 また、emailphone、およびbirthdayのJoiルールを使用してschemaを定義します。

emailの制約は次のとおりです。

  • 有効なメール文字列である必要があります
  • 必須です

phoneの制約は次のとおりです。

  • XXX-XXX-XXXXの形式の数字を含む文字列である必要があります
  • 必須です

birthdayの制約は次のとおりです。

  • ISO8601形式の有効な日付である必要があります
  • 2004年1月1日以降にすることはできません
  • 必須ではありません

次に、検証の合格と不合格を処理します。

app.js

// ...

app.use('/', Routes);

app.post('/test', (req, res, next) => {
  // ...

  Joi.validate(data, schema, (err, value) => {
    const id = Math.ceil(Math.random() * 9999999);

    if (err) {
      res.status(422).json({
        status: 'error',
        message: 'Invalid request data',
        data: data
      });
    } else {
      res.json({
        status: 'success',
        message: 'User created successfully',
        data: Object.assign({id}, value)
      });
    }
  });
});

// establish http server connection
app.listen(port, () => { console.log(`App running on port ${port}`) });

このコードはdataを受け取り、schemaに対して検証します。

emailphone、またはbirthdayのいずれかのルールが失敗すると、ステータスが"error"で、メッセージが"Invalid request data"

emailphonebirthdayのすべてのルールに合格すると、ステータスが"success"、メッセージが"User created successfully"

これで、サンプルルートをテストできます。

端末から次のコマンドを実行して、アプリを再起動します。

npm start

Postmanを使用して、サンプルルートPOST /testをテストできます。

リクエストを設定します。

POST localhost:3000/test
Body
Raw
JSON

データをJSONフィールドに追加します。

{
    "email": "[email protected]",
    "phone": "555-555-5555",
    "birthday": "2004-01-01"
}

次のような応答が表示されるはずです。

Output{
    "status": "success",
    "message": "User created successfully",
    "data": {
        "id": 1234567,
        "email": "[email protected]",
        "phone": "555-555-5555",
        "birthday": "2004-01-01T00:00:00.000Z"
    }
}

これを実現するデモビデオは次のとおりです。

YouTubeビデオを見る

基本スキーマにさらに検証制約を指定して、有効と見なされる値の種類を制御できます。 各制約はスキーマインスタンスを返すため、メソッドチェーンを介して複数の制約をチェーンし、より具体的な検証ルールを定義することができます。

Joi.object()またはJoi.object().keys()を使用してオブジェクトスキーマを作成することをお勧めします。 これらの2つのメソッドのいずれかを使用する場合、オブジェクトリテラルメソッドを使用して行うことはできないいくつかの追加の制約を使用して、オブジェクトで許可されるキーをさらに制御できます。

場合によっては、値を文字列、数値、またはその他のものにする必要があります。 ここで、代替スキーマが役立ちます。 Joi.alternatives()を使用して代替スキーマを定義できます。 any()スキーマを継承しているため、required()などの制約を使用できます。

使用可能なすべての制約の詳細なドキュメントについては、APIリファレンスを参照してください。

ステップ3—APIスキーマを作成する

Joiの制約とスキーマに慣れたら、APIルートの検証スキーマを作成できます。

プロジェクトルートディレクトリにschemas.jsという名前の新しいファイルを作成します。

nano schemas.js

Joiを要求することから始めます:

schemas.js

// load Joi module
const Joi = require('joi');

peopleエンドポイントとpersonDataSchema

/peopleエンドポイントはpersonDataSchemaを使用します。 このシナリオでは、管理者が教師と生徒のアカウントを作成しています。 APIには、idtypename、および学生の場合はageが必要です。

id:UUID v4形式の文字列になります:

Joi.string().guid({version: 'uuidv4'})

typeSTUDENTまたはTEACHERの文字列になります。 検証はどのような場合でも受け入れますが、uppercase()を強制します。

Joi.string().valid('STUDENT', 'TEACHER').uppercase().required()

age6より大きい値の整数または文字列になります。 また、文字列には「年」の短縮形式(「y」、「yr」、「yrs」など)を含めることもできます。

Joi.alternatives().try([
  Joi.number().integer().greater(6).required(),
  Joi.string().replace(/^([7-9]|[1-9]\d+)(y|yr|yrs)?$/i, '$1').required()
]);

firstnamelastnamefullname:アルファベット文字の文字列になります。 検証はどのような場合でも受け入れますが、uppercase()を強制します。

firstnameおよびlastnameの英字の文字列:

Joi.string().regex(/^[A-Z]+$/).uppercase()

スペースで区切られたfullname

Joi.string().regex(/^[A-Z]+ [A-Z]+$/i).uppercase()

fullnameを指定する場合は、firstnameおよびlastnameを省略してください。 firstnameを指定する場合は、lastnameも指定する必要があります。 fullnameまたはfirstnameのいずれかを指定する必要があります。

.xor('firstname', 'fullname')
.and('firstname', 'lastname')
.without('fullname', ['firstname', 'lastname'])

すべてをまとめると、peopleDataSchemaは次のようになります。

schemas.js

// ...

const personID = Joi.string().guid({version: 'uuidv4'});

const name = Joi.string().regex(/^[A-Z]+$/).uppercase();

const ageSchema = Joi.alternatives().try([
  Joi.number().integer().greater(6).required(),
  Joi.string().replace(/^([7-9]|[1-9]\d+)(y|yr|yrs)?$/i, '$1').required()
]);

const personDataSchema = Joi.object().keys({
  id: personID.required(),
  firstname: name,
  lastname: name,
  fullname: Joi.string().regex(/^[A-Z]+ [A-Z]+$/i).uppercase(),
  type: Joi.string().valid('STUDENT', 'TEACHER').uppercase().required(),

  age: Joi.when('type', {
    is: 'STUDENT',
    then: ageSchema.required(),
    otherwise: ageSchema
  })
})
.xor('firstname', 'fullname')
.and('firstname', 'lastname')
.without('fullname', ['firstname', 'lastname']);

/auth/editエンドポイントとauthDataSchema

/auth/editエンドポイントはauthDataSchemaを使用します。 このシナリオでは、教師がアカウントの電子メールとパスワードを更新しています。 APIには、idemailpassword、およびconfirmPasswordが必要です。

idpersonDataSchemaに対して以前に定義された検証を使用します。

email:有効なメールアドレスになります。 検証はどのような場合でも受け入れますが、lowercase()を強制します。

Joi.string().email().lowercase().required()

password:少なくとも7文字の文字列になります:

Joi.string().min(7).required().strict()

confirmPasswordpasswordを参照する文字列になり、2つの一致を確認します。

Joi.string().valid(Joi.ref('password')).required().strict()

すべてをまとめると、authDataSchemaは次のようになります。

schemas.js

// ...

const authDataSchema = Joi.object({
  teacherId: personID.required(),
  email: Joi.string().email().lowercase().required(),
  password: Joi.string().min(7).required().strict(),
  confirmPassword: Joi.string().valid(Joi.ref('password')).required().strict()
});

/fees/payエンドポイントとfeesDataSchema

/fees/payエンドポイントはfeesDataSchemaを使用します。 このシナリオでは、学生が金額を支払うためにクレジットカード情報を送信し、トランザクションのタイムスタンプも記録されます。 APIには、idamountcardNumber、およびcompletedAtが必要です。

idpersonDataSchemaに対して以前に定義された検証を使用します。

amount:整数または浮動小数点数のいずれかになります。 値は、1より大きい正の数である必要があります。 浮動小数点数が指定されている場合、精度は最大2に切り捨てられます。

Joi.number().positive().greater(1).precision(2).required()

cardNumber:有効な Luhn Algorithm 準拠の番号である文字列になります:

Joi.string().creditCard().required()

completedAt:JavaScript形式の日付タイムスタンプになります:

Joi.date().timestamp().required()

すべてをまとめると、feesDataSchemaは次のようになります。

schemas.js

// ...

const feesDataSchema = Joi.object({
  studentId: personID.required(),
  amount: Joi.number().positive().greater(1).precision(2).required(),
  cardNumber: Joi.string().creditCard().required(),
  completedAt: Joi.date().timestamp().required()
});

最後に、スキーマに関連付けられているエンドポイントを持つオブジェクトをエクスポートします。

schemas.js

// ...

// export the schemas
module.exports = {
  '/people': personDataSchema,
  '/auth/edit': authDataSchema,
  '/fees/pay': feesDataSchema
};

これで、APIエンドポイントのスキーマが作成され、エンドポイントをキーとしてオブジェクトにエクスポートされました。

ステップ4—スキーマ検証ミドルウェアの作成

APIエンドポイントへのすべてのリクエストをインターセプトし、ルートハンドラーに制御を渡す前にリクエストデータを検証するミドルウェアを作成しましょう。

プロジェクトのルートディレクトリにmiddlewaresという名前の新しいフォルダを作成します。

mkdir middlewares

次に、その中にSchemaValidator.jsという名前の新しいファイルを作成します。

nano middlewares/SchemaValidator.js

このファイルには、スキーマ検証ミドルウェア用の次のコードが含まれている必要があります。

ミドルウェア/SchemaValidator.js

const _ = require('lodash');
const Joi = require('joi');
const Schemas = require('../schemas');

module.exports = (useJoiError = false) => {
  // useJoiError determines if we should respond with the base Joi error
  // boolean: defaults to false
  const _useJoiError = _.isBoolean(useJoiError) && useJoiError;

  // enabled HTTP methods for request data validation
  const _supportedMethods = ['post', 'put'];

  // Joi validation options
  const _validationOptions = {
    abortEarly: false,  // abort after the last validation error
    allowUnknown: true, // allow unknown keys that will be ignored
    stripUnknown: true  // remove unknown keys from the validated data
  };

  // return the validation middleware
  return (req, res, next) => {
    const route = req.route.path;
    const method = req.method.toLowerCase();

    if (_.includes(_supportedMethods, method) && _.has(Schemas, route)) {
      // get schema for the current route
      const _schema = _.get(Schemas, route);

      if (_schema) {
        // Validate req.body using the schema and validation options
        return Joi.validate(req.body, _schema, _validationOptions, (err, data) => {
          if (err) {
            // Joi Error
            const JoiError = {
              status: 'failed',
              error: {
                original: err._object,
                // fetch only message and type from each error
                details: _.map(err.details, ({message, type}) => ({
                  message: message.replace(/['"]/g, ''),
                  type
                }))
              }
            };

            // Custom Error
            const CustomError = {
              status: 'failed',
              error: 'Invalid request data. Please review request and try again.'
            };

            // Send back the JSON error response
            res.status(422).json(_useJoiError ? JoiError : CustomError);
          } else {
            // Replace req.body with the data after Joi validation
            req.body = data;
            next();
          }
        });
      }
    }
    next();
  };
};

ここでは、LodashをJoiおよびスキーマと一緒にミドルウェアモジュールにロードしました。 また、1つの引数を受け入れ、スキーマ検証ミドルウェアを返すファクトリ関数をエクスポートしています。

ファクトリ関数の引数はboolean値であり、trueの場合、Joi検証エラーを使用する必要があることを示します。 それ以外の場合は、ミドルウェアのエラーにカスタムの一般的なエラーが使用されます。 指定されていないか、ブール値以外の値が指定されている場合、デフォルトでfalseになります。

また、POSTおよびPUT要求のみを処理するようにミドルウェアを定義しました。 他のすべてのリクエストメソッドはミドルウェアによってスキップされます。 必要に応じて、リクエスト本文を受け取ることができるDELETEなどの他のメソッドを追加するように構成することもできます。

ミドルウェアは、前に定義したSchemasオブジェクトの現在のルートキーと一致するスキーマを使用して、要求データを検証します。 検証は、Joi.validate()メソッドを使用して、次の署名で行われます。

  • data:この場合はreq.bodyを検証するためのデータ。
  • schema:データを検証するためのスキーマ。
  • options:検証オプションを指定するobject。 使用した検証オプションは次のとおりです。
  • callback:検証後に呼び出されるコールバックfunction。 2つの引数が必要です。 1つ目は、検証エラーがあった場合はJoi ValidationErrorオブジェクト、エラーがなかった場合はnullです。 2番目の引数は出力データです。

最後に、Joi.validate()のコールバック関数で、エラーがある場合は422 HTTPステータスコードを含むJSON応答としてフォーマットされたエラーを返すか、req.bodyを単に上書きします。出力データを検証してから、制御を次のミドルウェアに渡します。

これで、ルートでミドルウェアを使用できます。

nano routes.js

routes.jsファイルを次のように変更します。

ルート.js

const express = require('express');
const router = express.Router();

const SchemaValidator = require('./middlewares/SchemaValidator');
const validateRequest = SchemaValidator(true);

// generic route handler
const genericHandler = (req, res, next) => {
  res.json({
    status: 'success',
    data: req.body
  });
};

// create a new teacher or student
router.post('/people', validateRequest, genericHandler);

// change auth credentials for teachers
router.post('/auth/edit', validateRequest, genericHandler);

// accept fee payments for students
router.post('/fees/pay', validateRequest, genericHandler);

module.exports = router;

アプリを実行して、アプリケーションをテストしてみましょう。

npm start

これらは、エンドポイントのテストに使用できるサンプルテストデータです。 好きなように編集できます。

注: UUID v4文字列を生成するには、ノードUUIDモジュールまたはオンラインUUIDジェネレーターを使用できます。


/peopleエンドポイント

このシナリオでは、管理者が12歳のJohnDoeという名前の新しい学生をシステムに入力しています。

{
    "id": "a967f52a-6aa5-401d-b760-35eef7c68b32",
    "type": "Student",
    "firstname": "John",
    "lastname": "Doe",
    "age": "12yrs"
}

POST /people成功応答:

Output{
    "status": "success",
    "data": {
        "id": "a967f52a-6aa5-401d-b760-35eef7c68b32",
        "type": "STUDENT",
        "firstname": "JOHN",
        "lastname": "DOE",
        "age": "12"
    }
}

この失敗したシナリオでは、管理者は必須のageフィールドに値を指定していません。

Output{
    "status": "failed",
    "error": {
        "original": {
            "id": "a967f52a-6aa5-401d-b760-35eef7c68b32",
            "type": "Student",
            "fullname": "John Doe",
        },
        "details": [
            {
                "message": "age is required",
                "type": "any.required"
            }
        ]
    }
}

/auth/editエンドポイント

このシナリオでは、教師がメールアドレスとパスワードを更新しています。

{
    "teacherId": "e3464323-22c1-4e31-9ac5-9bde207d61d2",
    "email": "[email protected]",
    "password": "password",
    "confirmPassword": "password"
}

POST /auth/edit成功応答:

Output{
    "status": "success",
    "data": {
        "teacherId": "e3464323-22c1-4e31-9ac5-9bde207d61d2",
        "email": "[email protected]",
        "password": "password",
        "confirmPassword": "password"
    }
}

この失敗したシナリオでは、教師が無効な電子メールアドレスと誤った確認パスワードを提供しました。

Output{
    "status": "failed",
    "error": {
        "original": {
            "teacherId": "e3464323-22c1-4e31-9ac5-9bde207d61d2",
            "email": "email_address",
            "password": "password",
            "confirmPassword": "Password"
        },
        "details": [
            {
                "message": "email must be a valid email",
                "type": "string.email"
            },
            {
                "message": "confirmPassword must be of [ref:password]",
                "type": "any.allowOnly"
            }
        ]
    }
}

/fees/payエンドポイント

このシナリオでは、学生はクレジットカードで料金を支払い、トランザクションのタイムスタンプを記録しています。

注:テストの目的で、有効なクレジットカード番号として4242424242424242を使用してください。 この番号は、Stripeなどのサービスによってテスト目的で指定されています。


{
    "studentId": "c77b8a6e-9d26-428a-9df1-e852473f886f",
    "amount": 134.9875,
    "cardNumber": "4242424242424242",
    "completedAt": 1512064288409
}

POST /fees/pay成功応答:

Output{
    "status": "success",
    "data": {
        "studentId": "c77b8a6e-9d26-428a-9df1-e852473f886f",
        "amount": 134.99,
        "cardNumber": "4242424242424242",
        "completedAt": "2017-11-30T17:51:28.409Z"
    }
}

この失敗したシナリオでは、学生は無効なクレジットカード番号を提供しました。

Output{
    "status": "failed",
    "error": {
        "original": {
            "studentId": "c77b8a6e-9d26-428a-9df1-e852473f886f",
            "amount": 134.9875,
            "cardNumber": "5678901234567890",
            "completedAt": 1512064288409
        },
        "details": [
            {
                "message": "cardNumber must be a credit card",
                "type": "string.creditCard"
            }
        ]
    }
}

さまざまな値を使用してアプリケーションのテストを完了し、検証の成功と失敗を確認できます。

結論

このチュートリアルでは、Joiを使用してデータのコレクションを検証するためのスキーマを作成し、HTTPリクエストパイプラインでカスタムスキーマ検証ミドルウェアを使用してリクエストデータの検証を処理しました。

一貫性のあるデータがあると、アプリケーションでデータを参照するときに、信頼性が高く期待どおりに動作することが保証されます。

このチュートリアルの完全なコードサンプルについては、GitHubのjoi-schema-validation-sourcecodeリポジトリを確認してください。