Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
amitbeck opened this issue Oct 28, 2024 · 1 comment
Labels
help wanted Extra attention is needed question Further information is requested

Comments

@amitbeck
Copy link

💬 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
@amitbeck amitbeck added the help wanted Extra attention is needed label Oct 28, 2024
@amitbeck amitbeck changed the title Update a route's request body schema according to the provided route param in runtime Update request body schema according to the provided route param in runtime Oct 28, 2024
@dosubot dosubot bot added the question Further information is requested label Oct 28, 2024
@metcoder95
Copy link
Member

I'm not an expert on zod, but that cannot be achieved directly in the schema definition to do a lookup for the property and use a variant?

At the moment the request is received, the context of the request is sealed and cannot be altered (which affects the schema). request.routeOptions properties are read-only.

This has to be achieved using the fastify.setValidationCompiler. There you can customize how the validation gets set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants