Skip to content

Update request body schema according to the provided route param in runtime #1067

Open
@amitbeck

Description

@amitbeck

💬 Question here

Is it possible to update a route's request body schema (request.routeOptions.schema.body) according to the provided route param in runtime, using the preValidation hook? It's possible to achieve this using the preHandler hook but that's past the builtin validation stage of the request, and requires manually validating the request body.

This is my schema:

import { type RouteShorthandOptions } from 'fastify';
import { z } from 'zod';

const userInteractionKindSchema = z.enum(['bookmark', 'reaction', 'feedback']);

const bookmarkPayloadSchema = z.object({
  bookmark: z.boolean(),
});
const reactionPayloadSchema = z.object({
  reaction: z.enum(['like', 'dislike']).nullable(),
});
const feedbackPayloadSchema = z.object({
  feedback: z.string(),
});

const userInteractionPayloadSchemaByKind = {
  [userInteractionKindSchema.enum.bookmark]: bookmarkPayloadSchema,
  [userInteractionKindSchema.enum.reaction]: reactionPayloadSchema,
  [userInteractionKindSchema.enum.feedback]: feedbackPayloadSchema,
};

const updateUserInteractionRouteSchema = {
  params: z.object({
    interactionKind: userInteractionKindSchema,
  }),
  body: z.union([
    bookmarkPayloadSchema,
    reactionPayloadSchema,
    feedbackPayloadSchema,
  ]),
} satisfies RouteShorthandOptions['schema'];

And this is my route:

import { type ZodTypeProvider } from 'fastify-type-provider-zod';

fastify.withTypeProvider<ZodTypeProvider>().put(
  '/userInteractions/:interactionKind',
  {
    schema: updateUserInteractionRouteSchema,
    // doesn't work
    preValidation: async request => {
      const interactionKindPayloadSchema = userInteractionPayloadSchemaByKind[
        request.params.interactionKind
      ];
      request.routeOptions.schema.body = interactionKindPayloadSchema /* requires `as any` to work */;
    },
    // works, but requires manual validation
    preHandler: async request => {
      const interactionKindPayloadSchema =
        userInteractionPayloadSchemaByKind[request.params.interactionKind];
      try {
        interactionKindPayloadSchema.parse(request.body);
      } catch (error) {
        throw new Error(
          'Invalid request body for the provided interaction kind',
          { cause: error },
        );
      }
    },
  },
  (request, reply) => { ... },
);

Updating request.routeOptions.schema.body in the preValidation hook doesn't seem to work - when getting a request for the "bookmark" interaction kind with { "reaction": "like" } as the body, which matches another interaction kind's schema, the handler is reached even though it should fail. My current workaround is to manually validate the request body in the preHandler hook.

Your Environment

  • node version: 20.10.0
  • fastify version: 4.24.3
  • os: Mac

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions