next-expose
Version:
A fluent, type-safe API routing and middleware layer for the Next.js App Router.
148 lines • 8.69 kB
TypeScript
/**
* Defines the supported HTTP methods for the router.
*/
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
/**
* The core context object passed through the middleware pipeline.
* It is progressively augmented by middleware functions.
* @template TParams - The expected shape of the route's URL parameters (e.g., `{ id: string }`).
*/
export interface ExposeContext<TParams extends Record<string, any> = Record<string, string | string[] | undefined>> {
/** The resolved URL parameters from the dynamic route, such as `[id]`. */
params: TParams;
}
/**
* The `next` function passed to a middleware. It must be called to continue the request pipeline.
* @template CtxAdditions - The type of the properties that the current middleware will add to the context for the next step.
*/
export type NextFunction<CtxAdditions = void> = (contextAdditions?: CtxAdditions) => Promise<Response>;
/**
* Represents a middleware function in the `expose` pipeline.
* A middleware must either call `next()` to pass control down the chain or return a `Response` to end the request.
* The signature `({ req, ctx, next })` promotes DX by allowing developers to destructure only needed arguments.
*
* @template CtxIn - The shape of the context object this middleware receives.
* @template CtxAdditions - The shape of the properties this middleware adds to the context for the next middleware or handler.
* @param {object} args - The middleware arguments.
* @param {Request} args.req - The original `Request` object.
* @param {CtxIn} args.ctx - The current, augmented `ExposeContext` object.
* @param {NextFunction<CtxAdditions>} args.next - The function to call to proceed to the next step in the pipeline.
*/
export interface ExposeMiddleware<CtxIn extends ExposeContext<any> = ExposeContext, CtxAdditions = void> {
(args: {
req: Request;
ctx: CtxIn;
next: NextFunction<CtxAdditions>;
}): Promise<Response>;
}
/**
* Represents the final handler function in the `expose` pipeline. It receives the fully augmented context
* from all preceding middleware and is responsible for returning the final `Response`.
*
* @template CtxIn - The final shape of the context object after all middleware have run.
* @param {object} args - The final handler arguments.
* @param {CtxIn} args.ctx - The fully augmented context object.
* @param {Request} args.req - The original Next.js `Request` object.
* @returns {Promise<Response> | Response} The final `Response`.
*/
export interface ExposeFinalHandler<CtxIn extends ExposeContext<any> = ExposeContext> {
(args: {
ctx: CtxIn;
req: Request;
}): Promise<Response> | Response;
}
/**
* The standard type for a Next.js App Router API Route Handler.
* This is the ultimate output of the `expose` pipeline builder, making it compatible with Next.js.
*
* --- CHANGE ---
* The context.params object is now a Promise in Next.js 15+, especially with Turbopack.
* The type has been updated to reflect this, which resolves the build error.
*/
export type NextJsRouteHandler<TParams extends Record<string, any> = Record<string, string | string[]>> = (req: Request, context: {
params: Promise<TParams>;
}) => Promise<Response>;
/**
* A utility type to compute the new context shape after a middleware runs.
* It merges the current context with the additions from the middleware, ensuring type safety.
* @template CurrentCtx - The context type before the middleware.
* @template HandlerCtxAdditions - The type of the data being added by the middleware.
* @private
*/
type EvolvedContext<CurrentCtx extends ExposeContext<any>, HandlerCtxAdditions> = HandlerCtxAdditions extends void ? CurrentCtx : HandlerCtxAdditions extends object ? ExposeContext<CurrentCtx['params']> & Omit<CurrentCtx, keyof HandlerCtxAdditions | 'params'> & HandlerCtxAdditions : CurrentCtx;
/**
* Builds the middleware chain for a specific HTTP method.
* This class is internal to the `expose` library's fluency.
* @template BuilderMethod - The HTTP method being built (e.g., 'GET').
* @template TParams - The shape of the route's URL parameters.
* @template CurrentPipelineContext - The current, evolving shape of the context object.
* @template ParentRouterMethods - The methods already configured on the parent router.
* @private
*/
declare class RouteMethodBuilder<BuilderMethod extends HttpMethod, TParams extends Record<string, any>, CurrentPipelineContext extends ExposeContext<TParams>, ParentRouterMethods extends HttpMethod> {
private parentRouter;
private httpMethod;
private readonly collectedMiddlewares;
constructor(parentRouter: ExposeRouter<TParams, ParentRouterMethods>, httpMethod: BuilderMethod);
/**
* Adds a middleware function to the request-handling pipeline for this specific HTTP method.
* @param middleware The `ExposeMiddleware` function to add.
* @returns The `RouteMethodBuilder` instance for further chaining, with an evolved context type.
*/
use<MiddlewareCtxAdditions>(middleware: ExposeMiddleware<CurrentPipelineContext, MiddlewareCtxAdditions>): RouteMethodBuilder<BuilderMethod, TParams, EvolvedContext<CurrentPipelineContext, MiddlewareCtxAdditions>, ParentRouterMethods>;
/**
* Defines the final handler for the request pipeline. This terminates the chain for this method.
* @param finalHandler The `ExposeFinalHandler` that will process the request after all middleware have run.
* It receives a single object argument for better ergonomics: `({ ctx, req }) => ...`.
* @returns The parent `ExposeRouter` instance, now configured with the new method handler.
*/
handle(finalHandler: ExposeFinalHandler<CurrentPipelineContext>): ExposeRouter<TParams, ParentRouterMethods | BuilderMethod>;
}
/**
* The main router class that collects handlers for different HTTP methods for a single API endpoint.
* @template TParams - The expected shape of the URL parameters for all routes defined on this instance.
* @template ConfiguredMethods - A union of HTTP methods that have been configured on this instance.
*/
declare class ExposeRouter<TParams extends Record<string, any> = Record<string, string | string[] | undefined>, ConfiguredMethods extends HttpMethod = never> {
private methodPipelines;
/**
* Registers a fully built method handler pipeline with the router instance.
* @param method - The HTTP method.
* @param pipeline - The compiled `NextJsRouteHandler`.
* @returns The `ExposeRouter` instance.
* @internal
*/
_registerMethod<M extends HttpMethod>(method: M, pipeline: NextJsRouteHandler<TParams>): ExposeRouter<TParams, ConfiguredMethods | M>;
/** Starts building a pipeline for the GET HTTP method. */
get(): RouteMethodBuilder<'GET', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the POST HTTP method. */
post(): RouteMethodBuilder<'POST', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the PUT HTTP method. */
put(): RouteMethodBuilder<'PUT', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the DELETE HTTP method. */
delete(): RouteMethodBuilder<'DELETE', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the PATCH HTTP method. */
patch(): RouteMethodBuilder<'PATCH', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the OPTIONS HTTP method. */
options(): RouteMethodBuilder<'OPTIONS', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/** Starts building a pipeline for the HEAD HTTP method. */
head(): RouteMethodBuilder<'HEAD', TParams, ExposeContext<TParams>, ConfiguredMethods>;
/**
* Finalizes the router configuration and returns an object containing the compiled route handlers,
* ready to be exported from a Next.js `route.ts` file.
* @example
* export const { GET, POST } = route().get(...).post(...).expose();
*/
expose(): {
[P in ConfiguredMethods]: NextJsRouteHandler<TParams>;
};
}
/**
* The main factory function to create a new `ExposeRouter` instance for defining an API endpoint.
* This is the entry point for building all API routes.
* @template TParams - The expected shape of the route's URL parameters (e.g., `{ id: string }`).
* @returns A new `ExposeRouter` instance.
*/
export declare function route<TParams extends Record<string, any> = Record<string, string | string[] | undefined>>(): ExposeRouter<TParams>;
export {};
//# sourceMappingURL=core.d.ts.map