ウェブエンジニア問題集
第5章

Fastifyのschema validation — JSON Schemaで入力値を検証する

4
この章の目次開く

Fastifyを学ぶうえで、schema は最重要ポイントです。ルートごとに「どんなリクエストを受け取るか」「どんなレスポンスを返すか」をJSON Schemaで定義できます。

Fastifyのschemaは、バリデーションのためだけでなく、APIの契約をルート定義の近くに置くための仕組みです。

bodyを検証する

Todo作成APIにschemaを付けます。

fastify.post('/todos', {
  schema: {
    body: {
      type: 'object',
      required: ['title'],
      additionalProperties: false,
      properties: {
        title: { type: 'string', minLength: 1, maxLength: 100 },
      },
    },
  },
}, async (request, reply) => {
  const todo = {
    id: todos.length + 1,
    title: request.body.title,
    completed: false,
  };
 
  todos.push(todo);
 
  return reply.code(201).send(todo);
});
js

title がない、空文字、長すぎる、余計なプロパティがある、といったリクエストはhandlerに届く前に弾かれます。

学習者学習者

handlerの中で if (!title) って書かなくてよくなるんですか?

先生先生

入口の形のチェックはschemaに寄せられます。handlerは「正しい形で届いた値をどう処理するか」に集中できます。

schemaの構文

構文: schema: { body, params, querystring, response }

キー検証対象
bodyリクエストボディPOST /todos のJSON
paramsパスパラメータ/todos/:idid
querystringクエリ文字列?completed=true
responseレスポンス本文200201 で返すJSON

戻り値: schema自体に戻り値はありません。Fastifyがルート登録時にschemaを読み取り、リクエスト処理時に検証します。

paramsとquerystringを検証する

/todos/:idid を整数として扱いたい場合、params にschemaを書きます。

fastify.get('/todos/:id', {
  schema: {
    params: {
      type: 'object',
      required: ['id'],
      properties: {
        id: { type: 'integer', minimum: 1 },
      },
    },
  },
}, async (request, reply) => {
  const todo = todos.find((item) => item.id === request.params.id);
 
  if (!todo) {
    return reply.code(404).send({ error: 'TODO_NOT_FOUND' });
  }
 
  return todo;
});
js

Fastifyはschemaに基づいて値を変換できるため、id は数値として扱えます。ただし、変換や検証の挙動は設定にも影響されるため、複雑な入力ではテストで確認しましょう。

クエリ文字列も同じです。

fastify.get('/todos', {
  schema: {
    querystring: {
      type: 'object',
      properties: {
        completed: { type: 'boolean' },
      },
    },
  },
}, async (request) => {
  if (typeof request.query.completed === 'boolean') {
    return todos.filter((todo) => todo.completed === request.query.completed);
  }
 
  return todos;
});
js

response schemaを書く

response を書くと、Fastifyはレスポンスのシリアライズにもschemaを使います。返す形が明確になり、不要なプロパティを外に出しにくくなります。

const todoSchema = {
  type: 'object',
  properties: {
    id: { type: 'integer' },
    title: { type: 'string' },
    completed: { type: 'boolean' },
  },
};
 
fastify.get('/todos', {
  schema: {
    response: {
      200: {
        type: 'array',
        items: todoSchema,
      },
    },
  },
}, async () => {
  return todos;
});
js

よく使うJSON Schema

目的
文字列{ type: 'string', minLength: 1 }
数値{ type: 'integer', minimum: 1 }
真偽値{ type: 'boolean' }
必須{ required: ['title'] }
余計なキーを禁止{ additionalProperties: false }
配列{ type: 'array', items: itemSchema }

参考リンク

次章では、schemaで弾けない業務エラーや予期しない例外を、API全体でどう整えるかを学びます。

Node.jsクイズに挑戦するJSON、HTTP、エラー処理の基礎をクイズで確認しよう