fastify-type-provider-zod icon indicating copy to clipboard operation
fastify-type-provider-zod copied to clipboard

Schemas and hooks

Open sbeben opened this issue 1 year ago • 5 comments

I have custom JwtAuth hook and I use it on specific routes.

export const jwtAuth: preValidationHookHandler = (request, res, next) => {
  if (request.headers.cookie) {
    try {
      const token = request.headers.cookie.replace("token=", "");
      if (token && typeof token === "string") {
        const user = request.server.jwt.verify(token);
        request.user = user;
        next();
      }
    } catch (err) {
      res.status(401).send({ error: err });
    }
  }
  if (!request.headers.cookie)
    res.status(401).send({ message: "Unauthorized" });
};

Before I started to use ZodTypeProvider the type infered from fastify/jwt module where it has been manually set. So, I used it the following way and it obviosly worked.

 fastify.get<{ Params: ProfileParams }>(
    "/profile/:id",
    { preValidation: jwtAuth },
    async (req, res) => {
      const { id } = req.params;
      const userId = id === "me" ? req.user.id : id; 
      ...
    }
  );

But now when I switched to ".withTypeProvider<ZodTypeProvider>()" routes, the ones with preValidation hooks stopped to get the types so following is not working anymore, cuz for some reason ts recognizes now request as socket stream and params is unknown.

fastify.withTypeProvider<ZodTypeProvider>().get(
    "/profile/:id",
    {
      preValidation: jwtAuth,
      schema: {
        params: ProfileSchema,
        response: { 200: ProfileSuccess },
      },
    },
    async (req, res) => {
      const { id } = req.params; //params is unknown
      const userId = id === "me" ? req.user.id : id; //Property 'user' does not exist on type 'SocketStream'.
     ...
    }
  );

If i comment out the hook in last code example the type of params gets infered correctly!

I've tried dozens of workarounds already but did not find the solution to make this work. May be someone knows what am I missing?

sbeben avatar Mar 15 '23 19:03 sbeben

@sbeben try this


export type preHandlerAsyncHookZodHandler = preHandlerAsyncHookHandler<
  RawServerDefault,
  RawRequestDefaultExpression,
  RawReplyDefaultExpression,
  RouteGenericInterface,
  unknown,
  any,
  ZodTypeProvider
>;

sidwebworks avatar Mar 27 '23 09:03 sidwebworks

@sidwebworks I have the same problem. Could you tell me more about it?

yanoryuichi avatar Feb 09 '24 01:02 yanoryuichi

I cannot explain why, but in my case

We loose schema types : preHandler: validateRole,

Doesn't loose schema types : preHandler: (request, reply) => validateRole(request, reply)

Doesn't loose schema types : preHandler: [validateRole] as never,

actalexandre avatar Mar 19 '24 10:03 actalexandre

@sbeben try this

export type preHandlerAsyncHookZodHandler = preHandlerAsyncHookHandler<
  RawServerDefault,
  RawRequestDefaultExpression,
  RawReplyDefaultExpression,
  RouteGenericInterface,
  unknown,
  any,
  ZodTypeProvider
>;

@sbeben can you explain how to use it please.

i'm having same problem

const isAdmin = function prehandler(req: any, reply: any, done: any): asserts req is FastifyRequest & {user: IAdmin} {
  if (!req.user?.sub) throw new UnauthorizedError()
  if (req.user.role !== 'admin') throw new ForbiddenError()

  done()
}

but still in handler it see user?: JWT

alzalabany avatar Mar 23 '24 18:03 alzalabany

I solved it by creating custom types passing "ZodTypeProvider" as generic for the original fastify types.

fastify-zod-provider.d.ts

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

export type FastifyZodInstance = FastifyInstance<
  RawServer,
  RawRequest,
  RawReply,
  Logger,
  ZodTypeProvider
>;

export type FastifyZodRequest = FastifyRequest<
  RouteGeneric,
  RawServer,
  RawRequest,
  SchemaCompiler,
  ZodTypeProvider
>;

export type FastifyZodReply = FastifyReply<
  RawServer,
  RawRequest,
  RawReply,
  RouteGeneric,
  ContextConfig,
  SchemaCompiler,
  ZodTypeProvider
>;

my-fastify-hook.ts

export async function myFastifyHook(req: FastifyZodRequest, res: FastifyZodReply) {
 // your hook handler
}

my-fastify-route.ts

app.withTypeProvider<ZodTypeProvider>().route({
    preHandler: myFastifyHook,
    // another route settings
});

Leo-Henrique avatar May 18 '24 12:05 Leo-Henrique