Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Response schemas and serialization

Serialization is turning a JavaScript object into a JSON string before sending it to the client. If you describe a response schema, Fastify builds the JSON from it: this is faster than the usual JSON.stringify and also protects against leaking fields that are not in the schema.

A response schema

Response schemas go in schema.response, separately for each status code. Suppose posts in src/routes/posts.js got an internal authorEmail field that clients should not see:

// src/routes/posts.js
const posts = [
  { id: 1, title: 'First post', body: 'Text', authorEmail: 'anna@example.com' },
];
 
const showSchema = {
  params: {
    type: 'object',
    properties: { id: { type: 'integer' } },
  },
  response: {
    200: {
      type: 'object',
      properties: {
        id: { type: 'integer' },
        title: { type: 'string' },
        body: { type: 'string' },
      },
    },
  },
};
 
app.get('/posts/:id', { schema: showSchema }, async (request) => {
  return posts.find((post) => post.id === request.params.id);
});

Open http://localhost:3000/posts/1 — the reply contains only id, title and body. The authorEmail field is dropped because it is not in the schema.

A list

For an array, set type: 'array' and the item schema in items:

// src/routes/posts.js
const listSchema = {
  response: {
    200: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          id: { type: 'integer' },
          title: { type: 'string' },
        },
      },
    },
  },
};
 
app.get('/posts', { schema: listSchema }, async () => posts);

The /posts list now returns only id and title — post texts are needed only on the single-post page.

Shared schemas

To avoid repeating the post description in every route, register it once with addSchema and an $id. Add this to src/app.js before the routes are registered:

// src/app.js
app.addSchema({
  $id: 'post',
  type: 'object',
  properties: {
    id: { type: 'integer' },
    title: { type: 'string' },
    body: { type: 'string' },
  },
});

Now routes can refer to it with $ref:

// src/routes/posts.js
const showSchema = {
  response: {
    200: { $ref: 'post#' },
  },
};

The reply is the same, and the post description lives in one place.

Several codes

Key in responseWhen it is used
200A successful reply
201A reply after creating
'4xx'Any client error
defaultAll other codes

Common mistake

A new field that you added to the data but forgot in the response schema silently disappears from the JSON. If, for example, createdAt vanished from the reply without any error, check schema.response first.

Thanks for reading!

Donate to the site — we work hard for you.