Would you moved away from Express.JS to something like this?

Written by

in

Imagine a library for node.js that would have been completely decoupled from raw server request and response?

It may resemble koa a bit over its general vibe, I suppose.

Instead of raw request and response, only minimal information for each request is provided, and access to to raw request and response avoided, with special hatches to use them when the above interface is not enough (the feature methods on request and responder objects).

interface Request<TRouteParameters=Record<string,any>> {
   readonly id: string;
   method: string;
   url: string;
   host: string;
   query: ParsedQueryString;
   params:TRouteParameters;
   headers: RequestHeadersBag;
   cookies: RequestCookiesBag;
   contentLength: number;
   contentType: string;
   body: any;
   feature<T>(featureRef:RequestFeatureRef<T>):T;
   tryGetJson():Promise<any>;
   tryGetText():Promise<string>;
   tryGetBuffer():Promise<Buffer>;
   tryGeBodyStream: Readable;
}

interface Response {
   readonly statusCode: number;
   readonly headers: IResponseHeadersBag;
   readonly cookies: IResponseCookiesBag;
   readonly hasBody: boolean;
   execute(): Promise<void>;
}  

interface Responder
   readonly responded: boolean;
   status(status: number): Responder;
   setHeader(name:string, value:string):Responder;
   setCookie(cookie:CookieOptions):Responder;   
   
   next(): Promise<IResponse>;
   done(): IResponse;
   noContent() IResponse;
   redirect(url: string, reasonCode: RedirectReasonCode): IResponse;
   html(markup):IResponse;
   json(value:any):IResponse;
   stream(stream:Readable): Promise<IResponse>;
   feature<T>(featureRef:ResponseFeatureRef<T>):T;
}   

type RequestHandler = function(req:Request, res: Responder) => (Promise<Response>|Response|void);

export type AfterRequestHandler = (req: Request, res:Response) => (Promise<void>|void);

interface Routes {
   before(...handlers: RequestHandler[]): Routes;
   after(...afterHandler:AfterRequestHandler[]):Routes;
   use(path: RouteTemplateOrString, ...handlers: RequestHandler[]): Routes;   
   get(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   post(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   put(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   patch(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   delete(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   options(path: RouteTemplateOrString, ...handlers:RequestHandler): Routes;
   request(method:KnownHttpMethod, path: RouteTemplateOrString, ...handlers: RequestHandler): Routes;
   tryDispatch(request:Request):Promise<IResponse>;
   useRoutes(routes:Routes, prefix?:string):Routes;
}

//and something like this to use all that:
export function createRoutes(): Routes;
export function prepareForHttp(...routes: Routes[]): (req:IncomingMessage, res: ServerResponse) => void;

export function prepareForHttp2(...routes: Routes[]): (req:Http2ServerRequest, res: Http2ServerResponse) => void;
export function prepareMockRequest(options:MockRequestOptions):Request;
export function enableRequestFeature<TFeature>(factory:(req:Request) => T): RequestFeatureRef<T>;
export function enableResponseFeature<TFeature>(factory:(req:Request, res: Responder) => T):ResponseFeatureRef;

export const RawHttp2Request = RequestFeatureRef<Http2ServerRequest>;

export const RawHttp2Response = RequestFeatureRef<Http2ServerResponse>;

export const RawHttp1Request = RequestFeatureRef<IncomingMessage>;

export const RawHttp1Response = RequestFeatureRef<ServerResponse>;

I defined the interface above while wrote this post.

The reason for musing about this theoretical library is sort of being tired of not obvious ways to use same code without dirty hacks for http/1.1, http2, and tests.

Would you use such library? Or there is such library already?

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

To respond on your own website, enter the URL of your response which should contain a link to this post’s permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post’s URL again. (Find out more about Webmentions.)