fastify / fastify-type-provider-json-schema-to-ts

A Type Provider for json-schema-to-ts
MIT License
34 stars 8 forks source link

Unknown request body when using onRequest hook #17

Open gurumaxi opened 2 years ago

gurumaxi commented 2 years ago

Prerequisites

Issue

When using a hook like onRequest, the schema validation doesn't work anymore. If e.g. a body schema is specified in MySchema, the request body will be of type unknown:

const appWithTypeProvider = app.withTypeProvider<JsonSchemaToTsProvider>();
appWithTypeProvider.post('/', {schema: MySchema, onRequest: randomMethod}, async (request) => {
  request.body // unknown
});

Is this wanted behaviour?

Eomm commented 2 years ago

Notice: in the onRequest hook, request.body will always be undefined, because the body parsing happens before the preValidation hook.

https://www.fastify.io/docs/latest/Reference/Hooks/#onrequest

Checkout the hooks execution order here: https://www.fastify.io/docs/latest/Reference/Lifecycle/#lifecycle

gurumaxi commented 2 years ago

In this case, the body is also of type unknown when using a preHandler hook. According to https://www.fastify.io/docs/latest/Reference/Lifecycle/#lifecycle, the preHandler hook is called after the validation, so it should be defined right?

Eomm commented 2 years ago

the preHandler hook is called after the validation, so it should be defined right?

Yes, the body is set on that time

coocos commented 2 years ago

In this case, the body is also of type unknown when using a preHandler hook. According to https://www.fastify.io/docs/latest/Reference/Lifecycle/#lifecycle, the preHandler hook is called after the validation, so it should be defined right?

I stumbled on to the same issue with the preHandler hook. So if I define the schema and use an inline hook, the request body is inferred correctly in the actual handler:

server.post(
  "/items",
  {
    schema: {
      body: {
        type: "object",
        properties: {
          name: {
            type: "string",
          },
        },
        required: ["name"],
      } as const,
    },
    // Inline dummy hook
    preHandler: async (request, reply) => {},
  },
  async (request, reply) => {
    // No problems here, request.body.name is a string like it should be
    console.log(request.body.name);
  }
);

However, if I extract the hook into its own function (for reuse purposes and whatnot), then request.body is unknown:

const dummyHook = async (request: FastifyRequest, reply: FastifyReply) => {};

server.post(
  "/items",
  {
    schema: {
      body: {
        type: "object",
        properties: {
          name: {
            type: "string",
          },
        },
        required: ["name"],
      } as const,
    },
    // Hook is now a dedicated function
    preHandler: dummyHook 
  },
  async (request, reply) => {
    // TS2571: Object is of type 'unknown'.
    console.log(request.body.name);
  }
);

Is this expected behaviour? Reusing hooks sounds like a common scenario so I might be missing something. One way to work around this is to call the shared hook from the inline hook but it comes off as being a bit redundant:

  {
    schema: {
        ...
    },
    preHandler: async (request, reply) => {
      await dummyHook(request, reply);
    },
  },
  async (request, reply) => {
    // No problems here, request.body.name is a string like it should be
    console.log(request.body.name);
  }
SkeLLLa commented 1 year ago

I'm also facing issue described in last comment.

@Eomm maybe it worth to reopen this for visibility that specifying preHandler with dedicated function breaks typing? Or maybe just add a README note about "known issue" and workaround for it.

james-gardner commented 8 months ago

Please re-open. I've been hitting this issue as well and haven't found a feel-good work-around. @mcollina.

johanbook commented 3 months ago

Ran into this issue as well with type information being lost when using a prehandler that is an external method