ecyrbe / zodios

typescript http client and server with zod validation
https://www.zodios.org/
MIT License
1.67k stars 44 forks source link

How get type of request with context ? #443

Closed CarlosZiegler closed 1 year ago

CarlosZiegler commented 1 year ago

Hello,

First I want to say that is a really great library :)

In my case I like to use middlewares and controllers to hanle my request. Then is is great that I can do something like :

my context:

import { zodiosContext } from '@zodios/express';
import z from 'zod';
import { userSchema } from '../schemas/user.schema';

export const ctx = zodiosContext(
    z.object({
        user: userSchema,
    })
);

my route schema:

import { makeApi } from '@zodios/core';
import { userObjSchema } from '../schemas/user.schema';

export const userApi = makeApi([
    {
        method: 'get',
        path: '/',
        alias: 'iam',
        description: 'Get a current user',
        response: userObjSchema,
    },
]);

my route implementation:

That is fine if I do something like:

import { iamMiddleware } from '../middlewares';
import { userApi } from '../api/userApi';
import { ctx } from '../api/context.api';

const userRouter = ctx.router(userApi);

userRouter.use(iamMiddleware.iam);
userRouter.get('/', (req, res) => {
        // I get correct types here :). Thanks ZODIOS / ZOD
    const { id } = req.params;
    const user = req.user;
    res.json({
        user: user,
    });
});

But I want to extract this logic inside a controller:

my controller:

import { NextFunction, Request, Response } from 'express';
import { UserContext, UserRequest } from '../types';
import { userRepository } from '../repository';
import { User } from '@supabase/supabase-js';

// here is the problem, I dont get the types, need to be explicit, I can create a types but think is good that we
// use type generated of request on context. How can I get the request type of ctx ? 

const current = async (req, res) => {
    return res.status(200).json({ user: req.user });
};

Then will to use like:

import { userController } from '../controllers';
import { iamMiddleware } from '../middlewares';
import { userApi } from '../api/userApi';
import { ctx } from '../api/context.api';

const userRouter = ctx.router(userApi);

userRouter.use(iamMiddleware.iam);
userRouter.get('/', userController.current);

The main problem is if I need use the typed request or response on another piece of code, how can I get it ?

image

Thanks for some tips :)

lSelectral commented 1 year ago

Hello, I asked similar question before.

Here is how you can get typed middleware and controller type definition.

https://github.com/lSelectral/zodios-express-middleware-test/pull/1/commits/d25871cd969a28afbaa381db0736e249bef20203

export type Middleware<
    M extends Method,
    Path extends ZodiosPathsByMethod<Api, Method>
> = ZodiosRequestHandler<Api, Context, M, Path>

Sample

export const verifyPermissions = <
    M extends Method,
    Path extends ZodiosPathsByMethod<Api, Method>
>(
    ...permissions: PermissionType[]
) => {
    const middleware: Middleware<M, Path> = async (req, res, next) => {}}

Controller Sample

    public static create: Middleware<'post', '/booklets'> = async (
        req,
        res
    ) => {
        try {
            const result = await this.getService(req, res).create(req.body)
            return res.status(200).json(result)
        } catch (error) {
            return res.status(500).json(ServerError())
        }
    }