turkerdev / fastify-type-provider-zod

MIT License
281 stars 19 forks source link

Schemas and hooks #41

Open sbeben opened 1 year ago

sbeben commented 1 year ago

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()" 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?

sidwebworks commented 1 year ago

@sbeben try this


export type preHandlerAsyncHookZodHandler = preHandlerAsyncHookHandler<
  RawServerDefault,
  RawRequestDefaultExpression,
  RawReplyDefaultExpression,
  RouteGenericInterface,
  unknown,
  any,
  ZodTypeProvider
>;
yanoryuichi commented 5 months ago

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

actalexandre commented 3 months ago

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,

alzalabany commented 3 months ago

@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

Leo-Henrique commented 1 month ago

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
});