Configuration Options
You can provide configuration options, primarily hooks for handling errors and post-response actions, globally when creating/wrapping a router or on a per-route basis. Per-route options override global ones.
Global Options (TypedRouterOptions)
Passed as the optional second argument to createRouter or the
optional third argument to wrapRouter.
import express from 'express';
import * as t from 'io-ts'; // For Errors type
import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual
// Simplified representation of hook signatures
type DecodeErrorHandler<Req = any, Res = any> = (
errors: t.Errors,
req: express.Request & { decoded?: any }, // May not have decoded fully
res: express.Response & { sendEncoded?: any },
next: express.NextFunction,
) => void;
type EncodeErrorHandler<Req = any, Res = any> = (
error: unknown, // The error during encoding/validation
req: express.Request & { decoded?: any },
res: express.Response & { sendEncoded?: any },
next: express.NextFunction,
) => void;
type AfterResponseHandler<Req = any, Res = any> = (
status: number,
payload: any, // The successfully encoded payload
req: express.Request & { decoded?: any },
res: express.Response & { sendEncoded?: any },
) => void;
export type TypedRouterOptions<T extends ApiSpec = any> = {
onDecodeError?: DecodeErrorHandler;
onEncodeError?: EncodeErrorHandler;
afterEncodedResponseSent?: AfterResponseHandler;
};
onDecodeError(errors, req, res, next):- Triggered: When using a "checked" route method (such as
.get) and the incoming request fails decoding or validation against thehttpRoute'srequestcodec. - Purpose: Allows custom formatting and sending of error responses (such as 400 Bad Request). If not provided, a default basic error handler might be used or the error might propagate.
errors: Thet.Errorsarray fromio-tsdetailing the validation failures.- Note: You typically end the response (
res.status(...).json(...).end()) within this handler. Callingnext()might lead to unexpected behavior.
- Triggered: When using a "checked" route method (such as
onEncodeError(error, req, res, next):- Triggered: When
res.sendEncoded(status, payload)is called, but the providedpayloadfails validation against thehttpRoute'sresponsecodec for the givenstatus. - Purpose: Handles server-side errors where the application tries to send data inconsistent with the API specification. This usually indicates a bug.
error: The validation error encountered.- Note: You typically send a 500 Internal Server Error response here and should end the response.
- Triggered: When
afterEncodedResponseSent(status, payload, req, res):- Triggered: After
res.sendEncoded(status, payload)has successfully validated, encoded, and finished sending the response. - Purpose: Lets you perform side-effects after a successful response, such as logging, metrics collection, cleanup, etc.
status: The status code that was sent.payload: The original (pre-encoding) payload object that was sent.- Note: The response stream (
res) is likely ended at this point. Don't attempt to send further data.
- Triggered: After
Per-Route Options (RouteOptions)
Pass these as the optional third argument to the route definition methods (such as
typedRouter.get(..., ..., routeOptions)).
// RouteOptions includes the global hooks plus routeAliases
export type RouteOptions<RouteDef = any> = TypedRouterOptions & {
routeAliases?: string[];
};
onDecodeError/onEncodeError/afterEncodedResponseSent: Same hooks as the global options, but these versions apply only to the specific route they're defined on and take precedence over any global hooks defined viacreateRouterorwrapRouter.routeAliases(string[]):- An array of additional path strings that should also map to this route handler.
- Uses Express path syntax (such as
/path/:param). - See
TypedRouterObject for more details and caveats regarding path parameters.
Example (Global and Per-Route):
import { createRouter } from '@api-ts/typed-express-router';
import { MyApi } from 'my-api-package';
// Global options
const typedRouter = createRouter(MyApi, {
onDecodeError: globalDecodeErrorHandler,
afterEncodedResponseSent: globalMetricsHandler,
});
// Per-route options overriding global and adding alias
typedRouter.get('some.operation', [myHandler], {
routeAliases: ['/legacy/path'],
onDecodeError: specificDecodeErrorHandler, // Overrides globalDecodeErrorHandler for this route
// afterEncodedResponseSent is inherited from global options
});
typedRouter.post('another.operation', [otherHandler], {
// Inherits onDecodeError from global options
// No afterEncodedResponseSent hook will run for this route
afterEncodedResponseSent: undefined, // Explicitly disable global hook for this route
});