TypedRouter Object
The TypedRouter is the specialized Express Router object returned by
createRouter and wrapRouter. It exposes methods
for defining routes that are linked to operations in an ApiSpec, providing type safety
for requests and responses.
It largely mirrors the standard express.Router API but provides typed versions of HTTP
method functions (get, post, put, delete, patch, etc.) and specialized
unchecked variants.
Checked Route Methods
These methods (such as .get, .post, .put, etc.) add route handlers linked to a
specific operation name defined in the ApiSpec. They automatically handle request
decoding and validation based on the httpRoute definition.
Signature Example (.get)
import { TypedRequestHandler } from './typed-request-handler';
import { RouteOptions } from './configuration';
import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual
type TypedRouter<T extends ApiSpec> = {
get<OperationName extends keyof T & string>( // Restrict to string keys of the ApiSpec
operationName: OperationName,
handlers: Array<TypedRequestHandler<T[OperationName]['get']>>, // Type handlers based on the specific HttpRoute
routeOptions?: RouteOptions<T[OperationName]['get']>,
): this;
// Similar signatures for .post, .put, .delete, .patch, etc.
// ... other express.Router methods like .use
};
Parameters:
operationName(string): The key (operation name) from theApiSpeccorresponding to thehttpRoutedefinition for this endpoint.handlers(Array<TypedRequestHandler<...>>): An array of one or more request handler functions. These handlers receive augmentedreqandresobjects. See Augmented Request & Response andTypedRequestHandler.routeOptions(OptionalRouteOptions<...>): An optional object containing route-specific configuration, includingrouteAliasesand hooks that override global ones. See Configuration Options.
Behavior:
- The router registers the handlers for the path defined in the
httpRouteassociated with theoperationName. - The router adds middleware internally to automatically decode and validate incoming
requests against the
httpRoute'srequestcodec. - If decoding/validation succeeds, the router populates
req.decodedwith the result and calls the providedhandlers. - If decoding/validation fails, the router prevents the
handlersfrom being called and invokes theonDecodeErrorhook (either route-specific or global).
Route Aliases (routeOptions.routeAliases)
- You can provide an array of alternative path strings in
routeOptions.routeAliases. These paths will also route to the same handlers. - These alias paths use standard Express path syntax (including parameters like
:id). - Important: Ensure any path parameters defined in the
httpRoute's original path are also present in the alias paths if yourrequestcodec expects them inreq.decoded.params. If they're missing, decoding will likely fail.
Example
// Route handles both '/api/v1/item/{id}' (from spec) and '/api/item/:id' (alias)
typedRouter.get('api.v1.getItem', [getItemHandler], {
routeAliases: ['/api/item/:id'], // Express syntax for path param
});
Unchecked Route Methods
These methods (such as .getUnchecked, .postUnchecked, etc.) also add route handlers
linked to an ApiSpec operation, but they don't automatically trigger the
onDecodeError hook if request decoding fails.
Signature Example (.getUnchecked)
import express from 'express';
import { RouteOptions } from './configuration';
import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual
import * as E from 'fp-ts/Either';
import * as t from 'io-ts'; // For Errors type
type UncheckedRequestHandler = (
req: express.Request & { decoded: E.Either<t.Errors, any> }, // req.decoded is Either
res: express.Response & { sendEncoded: (...args: any[]) => void }, // res is still augmented
next: express.NextFunction,
) => void;
type TypedRouter<T extends ApiSpec> = {
getUnchecked<OperationName extends keyof T & string>(
operationName: OperationName,
handlers: Array<UncheckedRequestHandler>, // Use standard or custom handler type
routeOptions?: RouteOptions<T[OperationName]['get']>,
): this;
// Similar signatures for .postUnchecked, .putUnchecked, etc.
// ...
};
Behavior:
- The router registers handlers similarly to checked methods.
- The router attempts to decode the request internally.
- The router populates
req.decodedwith the result of the decoding attempt, which is of typeEither<Errors, DecodedRequest>fromfp-ts/Either. Errors is fromio-ts. - The router always calls the provided
handlers, regardless of whether decoding succeeded (isRight) or failed (isLeft). - The handler is responsible for checking the state of
req.decodedusingE.isLeftorE.isRightand acting accordingly.
Use Case: These methods let you handle invalid requests directly within the route
logic. You can log errors but still proceed, or return specific error formats without
relying on the global/route-specific onDecodeError hook.
Middleware (.use)
Middleware added via typedRouter.use() functions similarly to standard Express
middleware.
Behavior:
- Middleware handlers registered with
.userun after the initial request decoding attempt but before validation logic fully completes for checked routes. - Middleware handlers have access to
req.decodedcontaining theEither<Errors, DecodedRequest>, just like handlers added via.getUnchecked. This lets middleware inspect or react to the raw decoding result.
Example:
typedRouter.use((req, res, next) => {
// Can inspect the raw decode result here, even before checked routes
if (req.decoded && E.isLeft(req.decoded)) {
console.log('Middleware saw a decode failure');
}
next();
});
Other Methods
The TypedRouter object is compatible with the standard express.Router interface for
methods not explicitly overridden (like .param, .route, etc.). However, only routes
added via the typed methods (.get, .post, .getUnchecked, etc.) benefit from the
automatic decoding, augmented req/res, and hook system provided by this library.