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
'srequest
codec. - 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.Errors
array fromio-ts
detailing 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 providedpayload
fails validation against thehttpRoute
'sresponse
codec 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 viacreateRouter
orwrapRouter
.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
TypedRouter
Object 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
});