From 5da8ecabca70f1969f1c4d0485b65ee65c677ccf Mon Sep 17 00:00:00 2001 From: Itelo Filho Date: Fri, 14 Jul 2023 13:52:49 -0300 Subject: [PATCH] mirroring the interface --- apps/example-todo-app/src/app/edge/route.ts | 4 +- .../lib/middlewares/with-auth-token-edge.ts | 14 +- .../src/exceptions-middleware-egde/index.ts | 14 +- .../with-exception-handling.ts | 9 +- .../src/exceptions-middleware-nodejs/index.ts | 19 ++- packages/nextlove/src/types-edge/index.ts | 17 ++- .../src/with-route-spec-edge/index.ts | 19 ++- .../with-validation-edge.ts | 28 ++-- packages/nextlove/src/wrappers-edge.ts | 141 +++++++++++++----- 9 files changed, 168 insertions(+), 97 deletions(-) diff --git a/apps/example-todo-app/src/app/edge/route.ts b/apps/example-todo-app/src/app/edge/route.ts index fa91fade9..5f10ba6c0 100644 --- a/apps/example-todo-app/src/app/edge/route.ts +++ b/apps/example-todo-app/src/app/edge/route.ts @@ -10,6 +10,6 @@ const route_spec = { auth: "none", } as const -export const GET = withRouteSpecEdge(route_spec)((req) => { - return req.responseEdge.status(200).json({ return: true }) +export const GET = withRouteSpecEdge(route_spec)((req, res) => { + return res.status(200).json({ return: true }) }) diff --git a/apps/example-todo-app/src/lib/middlewares/with-auth-token-edge.ts b/apps/example-todo-app/src/lib/middlewares/with-auth-token-edge.ts index 8e4674126..069f8b3df 100644 --- a/apps/example-todo-app/src/lib/middlewares/with-auth-token-edge.ts +++ b/apps/example-todo-app/src/lib/middlewares/with-auth-token-edge.ts @@ -1,10 +1,14 @@ import { UnauthorizedException, MiddlewareEdge } from "nextlove" +import { NextloveRequest, NextloveResponse } from "nextlove/dist/edge-helpers" -export const withAuthTokenEdge: MiddlewareEdge<{ - auth: { - authorized_by: "auth_token" +export const withAuthTokenEdge: MiddlewareEdge = (next) => async (req) => { +> = (next) => async (req, res) => { const authorization = req.headers.get("authorization") if (authorization?.split("Bearer ")?.[1] !== "auth_token") { @@ -18,7 +22,7 @@ export const withAuthTokenEdge: MiddlewareEdge<{ authorized_by: "auth_token", } - return next(req) + return next(req, res) } export default withAuthTokenEdge diff --git a/packages/nextlove/src/exceptions-middleware-egde/index.ts b/packages/nextlove/src/exceptions-middleware-egde/index.ts index b3c508e62..c47add222 100644 --- a/packages/nextlove/src/exceptions-middleware-egde/index.ts +++ b/packages/nextlove/src/exceptions-middleware-egde/index.ts @@ -1,4 +1,4 @@ -import { NextloveRequest } from "../edge-helpers"; +import { NextloveRequest, NextloveResponse } from "../edge-helpers" import unwrappedWithExceptionHandlingEdge, { WithExceptionHandlingEdgeOptions, } from "./with-exception-handling" @@ -8,13 +8,13 @@ export interface ExceptionHandlingEdgeOptions { } export const withExceptionHandlingEdge = - ({ - exceptionHandlingOptions, - }: ExceptionHandlingEdgeOptions = {}) => + ({ exceptionHandlingOptions }: ExceptionHandlingEdgeOptions = {}) => (next: (req: NextloveRequest) => Promise) => - (req: NextloveRequest) => { - - return unwrappedWithExceptionHandlingEdge(exceptionHandlingOptions)(next)(req) + (req: NextloveRequest, res: NextloveResponse) => { + return unwrappedWithExceptionHandlingEdge(exceptionHandlingOptions)(next)( + req, + res + ) } export * from "../http-exceptions" diff --git a/packages/nextlove/src/exceptions-middleware-egde/with-exception-handling.ts b/packages/nextlove/src/exceptions-middleware-egde/with-exception-handling.ts index f8b4be872..5316294c5 100644 --- a/packages/nextlove/src/exceptions-middleware-egde/with-exception-handling.ts +++ b/packages/nextlove/src/exceptions-middleware-egde/with-exception-handling.ts @@ -1,4 +1,4 @@ -import { NextloveRequest } from "../edge-helpers" +import { NextloveRequest, NextloveResponse } from "../edge-helpers" import { HttpException } from "../http-exceptions" export interface WithExceptionHandlingEdgeOptions { @@ -10,11 +10,10 @@ export interface WithExceptionHandlingEdgeOptions { const withExceptionHandlingEdge = (options: WithExceptionHandlingEdgeOptions = {}) => - (next: (req: NextloveRequest) => Promise) => - async (req: NextloveRequest) => { - const res = req.responseEdge + (next: (req: NextloveRequest, res: NextloveResponse) => Promise) => + async (req: NextloveRequest, res: NextloveResponse) => { try { - return await next(req) + return await next(req, res) } catch (error: unknown) { let errorContext: any = {} diff --git a/packages/nextlove/src/exceptions-middleware-nodejs/index.ts b/packages/nextlove/src/exceptions-middleware-nodejs/index.ts index 07cd25585..75448bf3b 100644 --- a/packages/nextlove/src/exceptions-middleware-nodejs/index.ts +++ b/packages/nextlove/src/exceptions-middleware-nodejs/index.ts @@ -1,13 +1,13 @@ -import { NextApiRequest, NextApiResponse } from "next"; +import { NextApiRequest, NextApiResponse } from "next" import unwrappedWithExceptionHandling, { WithExceptionHandlingOptions, -} from "./with-exception-handling"; -import withOkStatus, { WithOkStatusOptions } from "./with-ok-status"; +} from "./with-exception-handling" +import withOkStatus, { WithOkStatusOptions } from "./with-ok-status" export interface ExceptionHandlingOptions { - addOkStatus?: boolean; - okStatusOptions?: WithOkStatusOptions; - exceptionHandlingOptions?: WithExceptionHandlingOptions; + addOkStatus?: boolean + okStatusOptions?: WithOkStatusOptions + exceptionHandlingOptions?: WithExceptionHandlingOptions } export const withExceptionHandling = @@ -21,12 +21,11 @@ export const withExceptionHandling = if (addOkStatus) { return withOkStatus(okStatusOptions)( unwrappedWithExceptionHandling(exceptionHandlingOptions)(next) - )(req, res); + )(req, res) } return unwrappedWithExceptionHandling(exceptionHandlingOptions)(next)( req, res - ); - }; - + ) + } diff --git a/packages/nextlove/src/types-edge/index.ts b/packages/nextlove/src/types-edge/index.ts index 61da2f532..7b57a46dc 100644 --- a/packages/nextlove/src/types-edge/index.ts +++ b/packages/nextlove/src/types-edge/index.ts @@ -13,7 +13,7 @@ export interface RouteSpecEdge< JsonBody extends ParamDef = z.ZodObject, QueryParams extends ParamDef = z.ZodObject, CommonParams extends ParamDef = z.ZodObject, - Middlewares extends readonly MiddlewareEdge[] = any[], + Middlewares extends readonly MiddlewareEdge[] = any[], JsonResponse extends ParamDef = z.ZodObject, FormData extends ParamDef = z.ZodTypeAny > { @@ -29,25 +29,25 @@ export interface RouteSpecEdge< } export type MiddlewareEdgeChainOutput< - MWChain extends readonly MiddlewareEdge[] + MWChain extends readonly MiddlewareEdge[] > = MWChain extends readonly [] ? {} : MWChain extends readonly [infer First, ...infer Rest] - ? First extends MiddlewareEdge + ? First extends MiddlewareEdge ? T & - (Rest extends readonly MiddlewareEdge[] + (Rest extends readonly MiddlewareEdge[] ? MiddlewareEdgeChainOutput : never) : never : never export type AuthMiddlewaresEdge = { - [auth_type: string]: MiddlewareEdge + [auth_type: string]: MiddlewareEdge } export interface SetupParamsEdge< AuthMW extends AuthMiddlewaresEdge = AuthMiddlewaresEdge, - GlobalMW extends MiddlewareEdge[] = any[] + GlobalMW extends MiddlewareEdge[] = any[] > { authMiddlewareMap: AuthMW globalMiddlewares: GlobalMW @@ -99,13 +99,15 @@ export type RouteEdgeFunction< > = ( req: (SP["authMiddlewareMap"] & typeof defaultMiddlewareMap)[RS["auth"]] extends MiddlewareEdge< + any, + any, infer AuthMWOut, any > ? Omit & AuthMWOut & MiddlewareEdgeChainOutput< - RS["middlewares"] extends readonly MiddlewareEdge[] + RS["middlewares"] extends readonly MiddlewareEdge[] ? [...SP["globalMiddlewares"], ...RS["middlewares"]] : SP["globalMiddlewares"] > & { @@ -129,6 +131,7 @@ export type RouteEdgeFunction< ErrorNextloveResponseMethods } : `unknown auth type: ${RS["auth"]}. You should configure this auth type in your auth_middlewares w/ createWithRouteSpec, or maybe you need to add "as const" to your route spec definition.`, + res: NextloveResponse ) => NextResponse | Promise export type CreateWithRouteSpecEdgeFunction = < diff --git a/packages/nextlove/src/with-route-spec-edge/index.ts b/packages/nextlove/src/with-route-spec-edge/index.ts index 21a7460bb..559fa91df 100644 --- a/packages/nextlove/src/with-route-spec-edge/index.ts +++ b/packages/nextlove/src/with-route-spec-edge/index.ts @@ -1,6 +1,6 @@ import { wrappersEdge } from "../wrappers-edge" import { withValidationEdge } from "./with-validation-edge" -import { NextloveRequest, getResponse } from "../edge-helpers" +import { NextloveRequest, NextloveResponse, getResponse } from "../edge-helpers" import { CreateWithRouteSpecEdgeFunction, RouteSpecEdge } from "../types-edge" import { withExceptionHandlingEdge } from "../exceptions-middleware-egde" @@ -26,7 +26,7 @@ export const createWithRouteSpecEdge: CreateWithRouteSpecEdgeFunction = (( } = setupParams const withRouteSpec = (spec: RouteSpecEdge) => { - const createRouteExport = (userDefinedRouteFn: (req: NextloveRequest) => any) => { + const createRouteExport = (userDefinedRouteFn: (req: NextloveRequest, res: NextloveResponse) => any) => { const rootRequestHandler = async ( req: NextloveRequest, ) => { @@ -35,13 +35,22 @@ export const createWithRouteSpecEdge: CreateWithRouteSpecEdgeFunction = (( addOkStatus: setupParams.addOkStatus, }) + const res = req.responseEdge + authMiddlewareMap["none"] = (next) => next; const auth_middleware = authMiddlewareMap[spec.auth] if (!auth_middleware) throw new Error(`Unknown auth type: ${spec.auth}`) - return wrappersEdge( ...((exceptionHandlingMiddleware ? [exceptionHandlingMiddleware] @@ -61,7 +70,7 @@ export const createWithRouteSpecEdge: CreateWithRouteSpecEdgeFunction = (( shouldValidateGetRequestBody, }), userDefinedRouteFn - )(req) + )(req, res) } rootRequestHandler._setupParams = setupParams diff --git a/packages/nextlove/src/with-route-spec-edge/with-validation-edge.ts b/packages/nextlove/src/with-route-spec-edge/with-validation-edge.ts index fc68549b5..1ec352b5b 100644 --- a/packages/nextlove/src/with-route-spec-edge/with-validation-edge.ts +++ b/packages/nextlove/src/with-route-spec-edge/with-validation-edge.ts @@ -4,7 +4,7 @@ import { InternalServerErrorException, } from "../http-exceptions" import { isEmpty } from "lodash" -import { NextloveRequest } from "../edge-helpers" +import { NextloveRequest, NextloveResponse } from "../edge-helpers" import { parseQueryParams, zodIssueToString } from "../zod-helpers" export interface RequestInput< @@ -26,15 +26,15 @@ export interface RequestInput< // NOTE: we should be able to use the same validation logic for both the nodejs and edge runtime function validateJsonResponse( jsonResponse: JsonResponse | undefined, - req: NextloveRequest + res: NextloveResponse ) { - const original_res_json = req.responseEdge.json - const override_res_json: NextloveRequest["responseEdge"]["json"] = ( + const original_res_json = res.json + const override_res_json: NextloveResponse["json"] = ( body, params ) => { const is_success = - req.responseEdge.statusCode >= 200 && req.responseEdge.statusCode < 300 + res.statusCode >= 200 && res.statusCode < 300 if (!is_success) { return original_res_json(body, params) } @@ -52,7 +52,9 @@ function validateJsonResponse( return original_res_json(body, params) } - req.responseEdge.json = override_res_json + res.json = override_res_json + + return res; } export const withValidationEdge = @@ -72,7 +74,7 @@ export const withValidationEdge = > ) => (next) => - async (req: NextloveRequest) => { + async (req: NextloveRequest, res: NextloveResponse) => { if ( (input.formData && input.jsonBody) || (input.formData && input.commonParams) @@ -180,12 +182,8 @@ export const withValidationEdge = }) } - /** - * this will override the res.json method to validate the response - */ - if (input.shouldValidateResponses) { - validateJsonResponse(input.jsonResponse, req) - } - - return next(req) + return next( + req, + input.shouldValidateResponses ? validateJsonResponse(input.jsonResponse, res) : res + ) } diff --git a/packages/nextlove/src/wrappers-edge.ts b/packages/nextlove/src/wrappers-edge.ts index 50e57d9b9..bda93e8c1 100644 --- a/packages/nextlove/src/wrappers-edge.ts +++ b/packages/nextlove/src/wrappers-edge.ts @@ -1,4 +1,4 @@ -import type { NextRequest as Req } from "next/server" +// import type { NextRequest as Req } from "next/server" /* Wraps a function in layers of other functions, while preserving the input/output @@ -47,44 +47,56 @@ const withLoggedArguments = */ -export type MiddlewareEdge = ( - next: (req: Req & Dep & T) => any -) => (req: Req & Dep & T) => any +export type MiddlewareEdge = ( + next: (req: Req & Dep & T, res: Res) => any +) => (req: Req & Dep & T, res: Res) => any // Safer Middleware requires the use of extendRequest to ensure that the // new context (T) was actually added to the request. It's kind of annoying // to use in practice, so we don't use it for our Wrappers (yet) -export type SaferMiddlewareEgde = ( - next: (req: Req & Dep & T) => any -) => (req: Req & Dep) => any +export type SaferMiddlewareEgde = ( + next: (req: Req & Dep & T, res: Res) => any +) => (req: Req & Dep, res: Res) => any -export const extendRequestEdge = >(req: T, merge: K): T & K => { +export const extendRequestEdge = < + Req, + Res, + T extends Req, + K extends ArrayLike +>( + req: T, + merge: K +): T & K => { for (const [key, v] of Object.entries(merge)) { ;(req as any)[key] = v } return req as any } -type WrappersEdge1 = ( - mw1: MiddlewareEdge, - endpoint: (req: Req & Mw1RequestContext) => any -) => (req: Req) => any +type WrappersEdge1 = ( + mw1: MiddlewareEdge, + endpoint: (req: Req & Mw1RequestContext, res: Res) => any +) => (req: Req, res: Res) => any type WrappersEdge2 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, Mw2Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, - endpoint: (req: Req & Mw1RequestContext & Mw2RequestContext) => any -) => (req: Req) => any + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, + endpoint: (req: Req & Mw1RequestContext & Mw2RequestContext, res: Res) => any +) => (req: Req, res: Res) => any // TODO figure out how to do a recursive definition, or one that simplifies // these redundant WrappersEdge type WrappersEdge3 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, @@ -92,18 +104,23 @@ type WrappersEdge3 = < Mw3RequestContext, Mw3Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, mw3: MiddlewareEdge< + Req, + Res, Mw3RequestContext, Mw1RequestContext & Mw2RequestContext extends Mw3Dep ? Mw3Dep : never >, endpoint: ( - req: Req & Mw1RequestContext & Mw2RequestContext & Mw3RequestContext + req: Req & Mw1RequestContext & Mw2RequestContext & Mw3RequestContext, + res: Res ) => any -) => (req: Req) => any +) => (req: Req, res: Res) => any type WrappersEdge4 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, @@ -113,27 +130,35 @@ type WrappersEdge4 = < Mw4RequestContext, Mw4Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, mw3: MiddlewareEdge< + Req, + Res, Mw3RequestContext, Mw1RequestContext & Mw2RequestContext extends Mw3Dep ? Mw3Dep : never >, mw4: MiddlewareEdge< + Req, + Res, Mw4RequestContext, Mw1RequestContext & Mw2RequestContext & Mw3RequestContext extends Mw4Dep ? Mw4Dep : never >, endpoint: ( - req: Req & Mw1RequestContext & + req: Req & + Mw1RequestContext & Mw2RequestContext & Mw3RequestContext & - Mw4RequestContext + Mw4RequestContext, + res: Res ) => any -) => (req: Req) => any +) => (req: Req, res: Res) => any type WrappersEdge5 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, @@ -145,19 +170,25 @@ type WrappersEdge5 = < Mw5RequestContext, Mw5Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, mw3: MiddlewareEdge< + Req, + Res, Mw3RequestContext, Mw1RequestContext & Mw2RequestContext extends Mw3Dep ? Mw3Dep : never >, mw4: MiddlewareEdge< + Req, + Res, Mw4RequestContext, Mw1RequestContext & Mw2RequestContext & Mw3RequestContext extends Mw4Dep ? Mw4Dep : never >, mw5: MiddlewareEdge< + Req, + Res, Mw5RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -167,15 +198,19 @@ type WrappersEdge5 = < : never >, endpoint: ( - req: Req & Mw1RequestContext & + req: Req & + Mw1RequestContext & Mw2RequestContext & Mw3RequestContext & Mw4RequestContext & - Mw5RequestContext + Mw5RequestContext, + res: Res ) => any -) => (req: Req) => any +) => (req: Req, res: Res) => any type WrappersEdge6 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, @@ -189,19 +224,25 @@ type WrappersEdge6 = < Mw6RequestContext, Mw6Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, mw3: MiddlewareEdge< + Req, + Res, Mw3RequestContext, Mw1RequestContext & Mw2RequestContext extends Mw3Dep ? Mw3Dep : never >, mw4: MiddlewareEdge< + Req, + Res, Mw4RequestContext, Mw1RequestContext & Mw2RequestContext & Mw3RequestContext extends Mw4Dep ? Mw4Dep : never >, mw5: MiddlewareEdge< + Req, + Res, Mw5RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -211,6 +252,8 @@ type WrappersEdge6 = < : never >, mw6: MiddlewareEdge< + Req, + Res, Mw6RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -221,16 +264,20 @@ type WrappersEdge6 = < : never >, endpoint: ( - req: Req & Mw1RequestContext & + req: Req & + Mw1RequestContext & Mw2RequestContext & Mw3RequestContext & Mw4RequestContext & Mw5RequestContext & - Mw6RequestContext + Mw6RequestContext, + res: Res ) => any -) => (req: Req) => any +) => (req: Req, res: Res) => any type WrappersEdge7 = < + Req, + Res, Mw1RequestContext extends Mw2Dep, Mw1Dep, Mw2RequestContext, @@ -246,19 +293,25 @@ type WrappersEdge7 = < Mw7RequestContext, Mw7Dep >( - mw1: MiddlewareEdge, - mw2: MiddlewareEdge, + mw1: MiddlewareEdge, + mw2: MiddlewareEdge, mw3: MiddlewareEdge< + Req, + Res, Mw3RequestContext, Mw1RequestContext & Mw2RequestContext extends Mw3Dep ? Mw3Dep : never >, mw4: MiddlewareEdge< + Req, + Res, Mw4RequestContext, Mw1RequestContext & Mw2RequestContext & Mw3RequestContext extends Mw4Dep ? Mw4Dep : never >, mw5: MiddlewareEdge< + Req, + Res, Mw5RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -268,6 +321,8 @@ type WrappersEdge7 = < : never >, mw6: MiddlewareEdge< + Req, + Res, Mw6RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -278,6 +333,8 @@ type WrappersEdge7 = < : never >, mw7: MiddlewareEdge< + Req, + Res, Mw7RequestContext, Mw1RequestContext & Mw2RequestContext & @@ -289,15 +346,17 @@ type WrappersEdge7 = < : never >, endpoint: ( - req: Req & Mw1RequestContext & + req: Req & + Mw1RequestContext & Mw2RequestContext & Mw3RequestContext & Mw4RequestContext & Mw5RequestContext & Mw6RequestContext & - Mw7RequestContext + Mw7RequestContext, + res: Res ) => any -) => (req: Req) => any +) => (req: Req, res: Res) => any type WrappersEdge = WrappersEdge1 & WrappersEdge2 & @@ -317,4 +376,4 @@ export const wrappersEdge: WrappersEdge = (...wrappersArgs: any[]) => { } return lastWrappedFunction -} \ No newline at end of file +}