import { NextApiHandler, NextApiRequest, NextApiResponse } from "next" // checkout NextJS 'NextApiResponse' generic, // TL;DR // calling response.send & response.json can be type, NextApiResponse interface API_Response { ok: boolean, status?: number, data?: any } interface ControllerConstructor { new (req: NextApiRequest, res: NextApiResponse): IController } // instance interface IController { req: NextApiRequest, res: NextApiResponse, controls: IControlTypes, exec(): Promise, setControls(controls: IControlTypes): void } // to execute export type IControlFunction = NextApiHandler // supported http verbs type IControlMethods = 'GET' | 'POST' | 'UPDATE' | 'DELETE' | 'PUT' | 'PATCH' | 'OPTIONS' // available controls type IControlTypes = { [methods in IControlMethods]?: IControlFunction; } export const Controller: ControllerConstructor = class Controller implements IController { controls: IControlTypes = {}; // constructor constructor(public req: NextApiRequest, public res: NextApiResponse){} // run single controller, [e.g. GET /api/users ] async exec() { try{ const { method } = this.req // get the controller according to http verb | method const control = this.controls[method as IControlMethods] if(!control){ // method not supported, end the request return this.res.status(405).end() } // execute the target control return await control(this.req,this.res) }catch(e){ // reject the request return this.res.status(500).json({ ok: false, status: 500, data: {} }) } } // set controllers, // controllers must be set before calling `await this.exec()` setControls(controls: IControlTypes){ this.controls = controls } }