From ae5f7e416ce551823d518915f32a7ae85d8bffab Mon Sep 17 00:00:00 2001 From: Young Jun Joo Date: Thu, 24 Apr 2025 12:04:58 -0400 Subject: [PATCH] =?UTF-8?q?docs(typed-express-router):=20Refactor=20docume?= =?UTF-8?q?ntation=20into=20Di=C3=A1taxis=20Reference=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit significantly restructures and enhances the documentation for the `@api-ts/typed-express-router` package by adopting the Diátaxis framework and creating a dedicated, multi-file Reference section. **Motivation:** The previous documentation, primarily located in the README, mixed introductory guides with technical details. This made it difficult for users to quickly find precise information about specific functions, types, or configuration options without reading through narrative content. A formal Reference section, following Diátaxis principles, provides a much clearer, more accessible, and maintainable structure for technical documentation. **Changes Implemented:** 1. **Diátaxis Reference Structure:** Established a dedicated directory (`docs/reference/typed-express-router/`) for technical reference material, separating it from other documentation types (like Tutorials or How-To Guides, which might exist elsewhere). 2. **Component-Focused File Organization:** Divided the reference documentation into distinct MDX files, each dedicated to a specific component or concept of the library: * `index.mdx`: Serves as the entry point and overview for the typed-express-router reference section. * `create-router.mdx`: Details the `createRouter` function. * `wrap-router.mdx`: Details the `wrapRouter` function. * `typed-router.mdx`: Describes the `TypedRouter` object itself, including its typed route methods (e.g., `.get`, `.getUnchecked`) and middleware handling (`.use`). * `request-response.mdx`: Explains the augmented `req` (with `req.decoded`) and `res` (with `res.sendEncoded`) objects provided to route handlers. * `configuration.mdx`: Documents the global (`TypedRouterOptions`) and per-route (`RouteOptions`) configuration, including error hooks (`onDecodeError`, `onEncodeError`), the post-response hook (`afterEncodedResponseSent`), and `routeAliases`. * `typed-request-handler.mdx`: Describes the `TypedRequestHandler` helper type for improved type safety in handler definitions. 3. **Content Migration & Refinement:** Technical information previously embedded in the README's "Usage" section has been extracted, expanded, and reformatted into precise reference documentation within the new structure. This includes: * Clear function/type signatures. * Detailed descriptions of parameters and return values. * Explicit explanations of component behavior (e.g., checked vs. unchecked routes, hook triggering conditions). * Code examples focused on illustrating the specific component being documented. **Benefits (Impact on Users & Maintainers):** * **Improved Discoverability:** Users needing technical details about `createRouter`, `req.decoded`, configuration hooks, or other specific features can now find dedicated pages quickly. * **Enhanced Clarity & Precision:** Technical specifications are presented directly and unambiguously, separate from introductory narratives. * **Better Maintainability:** Isolating documentation for each component makes future updates easier and less error-prone. * **Standardized Structure:** Adopts a recognized documentation pattern (Diátaxis), improving consistency potentially across multiple related packages. * **Increased Comprehensiveness:** Provides more explicit detail on the library's features and types than previously available in the consolidated README. This refactoring represents a significant improvement in the quality, usability, and maintainability of the `@api-ts/typed-express-router` technical documentation. --- .../typed-express-router/configuration.md | 119 ++++++++++++++ .../typed-express-router/create-router.md | 52 ++++++ .../reference/typed-express-router/index.md | 25 +++ .../typed-express-router/request-response.md | 88 ++++++++++ .../typed-request-handler.md | 82 ++++++++++ .../typed-express-router/typed-router.md | 153 ++++++++++++++++++ .../typed-express-router/wrap-router.md | 60 +++++++ website/sidebars.js | 16 ++ 8 files changed, 595 insertions(+) create mode 100644 website/docs/reference/typed-express-router/configuration.md create mode 100644 website/docs/reference/typed-express-router/create-router.md create mode 100644 website/docs/reference/typed-express-router/index.md create mode 100644 website/docs/reference/typed-express-router/request-response.md create mode 100644 website/docs/reference/typed-express-router/typed-request-handler.md create mode 100644 website/docs/reference/typed-express-router/typed-router.md create mode 100644 website/docs/reference/typed-express-router/wrap-router.md diff --git a/website/docs/reference/typed-express-router/configuration.md b/website/docs/reference/typed-express-router/configuration.md new file mode 100644 index 00000000..5311be76 --- /dev/null +++ b/website/docs/reference/typed-express-router/configuration.md @@ -0,0 +1,119 @@ +# 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`](./create-router) or the +optional third argument to [`wrapRouter`](./wrap-router). + +```typescript +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 = ( + errors: t.Errors, + req: express.Request & { decoded?: any }, // May not have decoded fully + res: express.Response & { sendEncoded?: any }, + next: express.NextFunction, +) => void; + +type EncodeErrorHandler = ( + error: unknown, // The error during encoding/validation + req: express.Request & { decoded?: any }, + res: express.Response & { sendEncoded?: any }, + next: express.NextFunction, +) => void; + +type AfterResponseHandler = ( + status: number, + payload: any, // The successfully encoded payload + req: express.Request & { decoded?: any }, + res: express.Response & { sendEncoded?: any }, +) => void; + +export type TypedRouterOptions = { + 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 the `httpRoute`'s `request` 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`: The `t.Errors` array from `io-ts` detailing the validation failures. + - **Note**: You typically end the response (`res.status(...).json(...).end()`) within + this handler. Calling `next()` might lead to unexpected behavior. +- `onEncodeError(error, req, res, next)`: + - **Triggered**: When `res.sendEncoded(status, payload)` is called, but the provided + `payload` fails validation against the `httpRoute`'s `response` codec for the given + `status`. + - **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. +- `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. + +## Per-Route Options (`RouteOptions`) + +Pass these as the optional third argument to the route definition methods (such as +`typedRouter.get(..., ..., routeOptions)`). + +```typescript +// RouteOptions includes the global hooks plus routeAliases +export type RouteOptions = 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 via `createRouter` or `wrapRouter`. +- `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](./typed-router) for more details and caveats regarding + path parameters. + +## Example (Global and Per-Route): + +```typescript +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 +}); +``` diff --git a/website/docs/reference/typed-express-router/create-router.md b/website/docs/reference/typed-express-router/create-router.md new file mode 100644 index 00000000..add602d1 --- /dev/null +++ b/website/docs/reference/typed-express-router/create-router.md @@ -0,0 +1,52 @@ +# `createRouter` + +Creates a new Express Router instance that's typed according to a provided +`@api-ts/io-ts-http` API specification. + +**Signature:** + +```typescript +import express from 'express'; +import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual import +import { TypedRouter } from './typed-router'; // Conceptual import of the return type +import { TypedRouterOptions } from './configuration'; // Conceptual import + +declare function createRouter( + apiSpec: T, + options?: TypedRouterOptions, // Global options/hooks +): TypedRouter; // Returns the specialized router object +``` + +**Parameters:** + +- `apiSpec` (`ApiSpec`): An API specification object created using + `@api-ts/io-ts-http`'s `apiSpec` function. This defines the routes that you can attach + to this router. +- `options` (Optional `TypedRouterOptions`): An optional object containing global + configuration hooks for error handling and post-response actions. See + [Configuration Options](./configuration) for details. + +**Return Value:** + +- `TypedRouter`: A specialized Express Router instance. This object has methods (like + `.get`, `.post`) that accept operation names from the `apiSpec` and provide augmented + `req` and `res` objects to the handlers. See [`TypedRouter` Object](./typed-router) + for details. + +**Usage Example:** + +```typescript +import express from 'express'; +import { createRouter } from '@api-ts/typed-express-router'; +import { MyApi } from 'my-api-package'; // Your apiSpec import + +const app = express(); +const typedRouter = createRouter(MyApi, { + // Optional global configuration + onDecodeError: (errs, req, res, next) => { + res.status(400).json({ error: 'Invalid request format', details: errs }); + }, +}); + +app.use('/api', typedRouter); // Mount the typed router +``` diff --git a/website/docs/reference/typed-express-router/index.md b/website/docs/reference/typed-express-router/index.md new file mode 100644 index 00000000..fe3ded96 --- /dev/null +++ b/website/docs/reference/typed-express-router/index.md @@ -0,0 +1,25 @@ +# Reference: @api-ts/typed-express-router + +This section provides detailed technical descriptions of the functions, objects, types, +and configuration options available in the `@api-ts/typed-express-router` package. Use +this reference to understand the specific parameters, return values, and behavior of its +components when integrating `@api-ts/io-ts-http` specifications with Express. + +## Components + +- [**`createRouter`**](./create-router): Creates a new, typed Express Router instance + linked to an API specification. +- [**`wrapRouter`**](./wrap-router): Wraps an existing Express Router instance, linking + it to an API specification. +- [**`TypedRouter` Object**](./typed-router): Describes the router object returned by + `createRouter` and `wrapRouter`, detailing its route definition methods (`.get`, + `.post`, `.getUnchecked`, etc.) and middleware usage (`.use`). +- [**Augmented Request & Response**](./request-response): Explains the properties and + methods added to the standard Express `req` (`req.decoded`) and `res` + (`res.sendEncoded`) objects within typed route handlers. +- [**Configuration Options**](./configuration): Details the configurable options for + error handling (`onDecodeError`, `onEncodeError`), post-response actions + (`afterEncodedResponseSent`), and route aliasing (`routeAliases`). +- [**`TypedRequestHandler` Type**](./typed-request-handler): Describes the TypeScript + helper type for defining route handlers with correctly inferred augmented request and + response types. diff --git a/website/docs/reference/typed-express-router/request-response.md b/website/docs/reference/typed-express-router/request-response.md new file mode 100644 index 00000000..13a9023a --- /dev/null +++ b/website/docs/reference/typed-express-router/request-response.md @@ -0,0 +1,88 @@ +# Augmented Request & Response + +When you use route handlers registered via a [`TypedRouter`](./typed-router) object +(using methods like `.get`, `.post`, `.getUnchecked`, etc.), the standard Express +`request` and `response` objects are augmented with additional properties and methods +related to the API specification. + +## Augmented Request (`req`) + +The Express `request` object (`req`) passed to typed route handlers includes an +additional property: + +### `req.decoded` + +- **Type (Checked Routes):** `DecodedRequest` + - In handlers attached using the "checked" methods (such as `typedRouter.get(...)`), + `req.decoded` holds the successfully decoded and validated request data. Its + TypeScript type is inferred directly from the `request` codec defined in the + corresponding `httpRoute` of the `ApiSpec`. This object contains the flattened + combination of path parameters, query parameters, headers, and body properties as + defined by the `httpRequest` codec used in the spec. +- **Type (Unchecked Routes & Middleware):** `Either` + - In handlers attached using the "unchecked" methods (such as + `typedRouter.getUnchecked(...)`) or in middleware added via `typedRouter.use(...)`, + `req.decoded` holds the raw result of the decoding attempt from `io-ts`. This is an + `Either` type from the `fp-ts` library. + - Use `E.isRight(req.decoded)` to check if decoding was successful. If true, + `req.decoded.right` contains the `DecodedRequest`. + - Use `E.isLeft(req.decoded)` to check if decoding failed. If true, `req.decoded.left` + contains the `t.Errors` object detailing the validation failures. + +## Augmented Response (`res`) + +The Express `response` object (`res`) passed to typed route handlers includes an +additional method: + +### `res.sendEncoded(status, payload)` + +Use this method instead of `res.json()` or `res.send()` when sending responses that +should conform to the API specification. + +**Parameters:** + +- `status` (`number`): The HTTP status code for the response. This status code **must** + be a key defined in the `response` object of the `httpRoute` associated with the + current route in the `ApiSpec`. +- `payload` (`any`): The data to be sent as the response body. + +**Behavior:** + +1. **Type Checking:** Validates that the provided `payload` conforms to the `io-ts` + codec associated with the given `status` in the `httpRoute`'s `response` definition. +2. **Encoding:** Encodes the `payload` using the same `io-ts` codec. This handles + necessary transformations (such as converting a `Date` object to an ISO string if + using `DateFromISOString`, or a `bigint` to a string if using `BigIntFromString`). +3. **Sending Response:** Sets the response status code to `status`, sets the + `Content-Type` header to `application/json`, and sends the JSON-stringified encoded + payload as the response body. +4. **Error Handling:** If the `payload` fails validation against the codec for the + specified `status`, calls the `onEncodeError` hook (route-specific or global). +5. **Post-Response Hook:** After the response has been successfully sent, calls the + `afterEncodedResponseSent` hook (route-specific or global). + +**Example:** + +```typescript +import { TypedRequestHandler } from '@api-ts/typed-express-router'; +import { MyApi } from 'my-api-package'; + +// Assuming 'api.v1.getUser' route expects a { user: UserType } payload for status 200 +const getUserHandler: TypedRequestHandler = ( + req, + res, +) => { + const userId = req.decoded.userId; // Access decoded request data + const user = findUserById(userId); + + if (!user) { + // Assuming 404 is defined in the spec with an error object payload + res.sendEncoded(404, { error: 'User not found' }); + return; + } + + // Send status 200 with the UserType payload + // 'sendEncoded' ensures 'user' matches the spec for status 200 + res.sendEncoded(200, { user: user }); +}; +``` diff --git a/website/docs/reference/typed-express-router/typed-request-handler.md b/website/docs/reference/typed-express-router/typed-request-handler.md new file mode 100644 index 00000000..c8d0f097 --- /dev/null +++ b/website/docs/reference/typed-express-router/typed-request-handler.md @@ -0,0 +1,82 @@ +# `TypedRequestHandler` Type + +A TypeScript helper type provided by `@api-ts/typed-express-router` to help you define +Express route handlers with correctly inferred types for the augmented `request` and +`response` objects. + +**Purpose:** + +When defining handlers for "checked" routes (such as using `typedRouter.get(...)`), this +type automatically infers: + +- The type of `req.decoded` based on the `request` codec of the specific `httpRoute` + linked via the `operationName`. +- The type signature of `res.sendEncoded`, ensuring the `payload` type is checked + against the appropriate `response` codec for the given `status` code from the + `httpRoute`. + +**Definition (Conceptual):** + +```typescript +import express from 'express'; +import { HttpRoute } from '@api-ts/io-ts-http'; // Conceptual import +import * as t from 'io-ts'; // For TypeOf and OutputOf + +// RouteDefinition represents the specific httpRoute object from the ApiSpec +// e.g., MyApi['my.operation']['get'] +type RouteDefinition = HttpRoute; + +// Extracts the decoded request type from the route's request codec +type DecodedRequest = t.TypeOf; + +// Represents the augmented response object +type TypedResponse = express.Response & { + sendEncoded( // Status must be a key in response obj + status: Status, + // Payload type must match the codec for the given status + payload: t.TypeOf, + ): TypedResponse; // Allows chaining like standard Express res +}; + +export type TypedRequestHandler = ( + req: express.Request & { decoded: DecodedRequest }, + res: TypedResponse, + next: express.NextFunction, +) => void | Promise; // Allow async handlers +``` + +(Note: The actual implementation may involve more complex generic constraints) + +**Usage:** Import the type and use it when defining your handler functions. Provide the +specific `httpRoute` definition type from your imported `ApiSpec` as the generic +argument. + +```typescript +import express from 'express'; +import { TypedRequestHandler } from '@api-ts/typed-express-router'; +import { MyApi } from 'my-api-package'; // Your generated ApiSpec object + +// Define the type for the specific route handler +type HelloWorldRouteHandler = TypedRequestHandler; +// ^------------------------------^ +// Generic argument points to the specific httpRoute definition in the spec + +const handler: HelloWorldRouteHandler = (req, res, next) => { + // req.decoded is strongly typed based on MyApi['hello.world']['get'].request + const name = req.decoded.name || 'World'; + + // Payload for status 200 is type-checked against MyApi['hello.world']['get'].response[200] + res.sendEncoded(200, { message: `Hello, ${name}!` }); + + // If status 400 was defined in the spec with a different payload type: + // const errorPayload = { error: 'Missing name' }; + // res.sendEncoded(400, errorPayload); // This would also be type-checked +}; + +// Use the handler +// typedRouter.get('hello.world', [handler]); +``` + +Using `TypedRequestHandler` significantly improves your developer experience by +providing type safety and autocompletion for the decoded request properties and the +`sendEncoded` payload within route handlers. diff --git a/website/docs/reference/typed-express-router/typed-router.md b/website/docs/reference/typed-express-router/typed-router.md new file mode 100644 index 00000000..9d971636 --- /dev/null +++ b/website/docs/reference/typed-express-router/typed-router.md @@ -0,0 +1,153 @@ +# `TypedRouter` Object + +The `TypedRouter` is the specialized Express Router object returned by +[`createRouter`](./create-router) and [`wrapRouter`](./wrap-router). 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`)** + +```typescript +import { TypedRequestHandler } from './typed-request-handler'; +import { RouteOptions } from './configuration'; +import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual + +type TypedRouter = { + get( // Restrict to string keys of the ApiSpec + operationName: OperationName, + handlers: Array>, // Type handlers based on the specific HttpRoute + routeOptions?: RouteOptions, + ): this; + // Similar signatures for .post, .put, .delete, .patch, etc. + // ... other express.Router methods like .use +}; +``` + +**Parameters:** + +- `operationName` (`string`): The key (operation name) from the `ApiSpec` corresponding + to the `httpRoute` definition for this endpoint. +- `handlers` (`Array>`): An array of one or more request + handler functions. These handlers receive augmented `req` and `res` objects. See + [Augmented Request & Response](./request-response) and `TypedRequestHandler`. +- `routeOptions` (Optional `RouteOptions<...>`): An optional object containing + route-specific configuration, including `routeAliases` and hooks that override global + ones. See [Configuration Options](./configuration). + +**Behavior:** + +1. The router registers the handlers for the path defined in the `httpRoute` associated + with the `operationName`. +2. The router adds middleware internally to automatically decode and validate incoming + requests against the `httpRoute`'s `request` codec. +3. If decoding/validation succeeds, the router populates `req.decoded` with the result + and calls the provided `handlers`. +4. If decoding/validation fails, the router prevents the `handlers` from being called + and invokes the `onDecodeError` hook (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 your `request` codec expects them in + `req.decoded.params`. If they're missing, decoding will likely fail. + +**Example** + +```typescript +// 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`) + +```typescript +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 }, // req.decoded is Either + res: express.Response & { sendEncoded: (...args: any[]) => void }, // res is still augmented + next: express.NextFunction, +) => void; + +type TypedRouter = { + getUnchecked( + operationName: OperationName, + handlers: Array, // Use standard or custom handler type + routeOptions?: RouteOptions, + ): this; + // Similar signatures for .postUnchecked, .putUnchecked, etc. + // ... +}; +``` + +**Behavior:** + +1. The router registers handlers similarly to checked methods. +2. The router attempts to decode the request internally. +3. The router populates `req.decoded` with the result of the decoding attempt, which is + of type `Either` from `fp-ts/Either`. Errors is from `io-ts`. +4. The router always calls the provided `handlers`, regardless of whether decoding + succeeded (`isRight`) or failed (`isLeft`). +5. The handler is responsible for checking the state of `req.decoded` using `E.isLeft` + or `E.isRight` and 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 `.use` run after the initial request decoding + attempt but before validation logic fully completes for checked routes. +- Middleware handlers have access to `req.decoded` containing the + `Either`, just like handlers added via `.getUnchecked`. This + lets middleware inspect or react to the raw decoding result. + +**Example:** + +```typescript +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. diff --git a/website/docs/reference/typed-express-router/wrap-router.md b/website/docs/reference/typed-express-router/wrap-router.md new file mode 100644 index 00000000..410a01e9 --- /dev/null +++ b/website/docs/reference/typed-express-router/wrap-router.md @@ -0,0 +1,60 @@ +# `wrapRouter` + +Wraps an existing Express Router instance, augmenting it with type-checking capabilities +based on a provided `@api-ts/io-ts-http` API specification. This lets you integrate +typed routes into an existing router setup. + +**Signature:** + +```typescript +import express from 'express'; +import { ApiSpec } from '@api-ts/io-ts-http'; // Conceptual import +import { TypedRouter } from './typed-router'; // Conceptual import of the return type +import { TypedRouterOptions } from './configuration'; // Conceptual import + +declare function wrapRouter( + router: express.Router, // The existing Express router + apiSpec: T, + options?: TypedRouterOptions, // Global options/hooks +): TypedRouter; // Returns the augmented router object +``` + +**Parameters:** + +- `router` (`express.Router`): An existing instance of an Express Router. +- `apiSpec` (`ApiSpec`): An API specification object created using + `@api-ts/io-ts-http`'s `apiSpec` function. +- `options` (Optional `TypedRouterOptions`): An optional object containing global + configuration hooks for error handling and post-response actions. These hooks apply + only to routes added via the returned `TypedRouter` interface, not to routes already + on the original router. See [Configuration Options](./configuration) for details. + +**Return Value:** + +- `TypedRouter`: The same router instance passed in (`router`), but augmented with + the typed methods (like `.get`, `.post`) described in + [`TypedRouter` Object](./typed-router). Calling these typed methods adds routes linked + to the `apiSpec`. The original router methods remain functional but without the typed + features. + +**Usage Example:** + +```typescript +import express from 'express'; +import { wrapRouter } from '@api-ts/typed-express-router'; +import { MyApi } from 'my-api-package'; // Your apiSpec import + +const app = express(); +const existingRouter = express.Router(); + +// Add some non-typed routes +existingRouter.get('/status', (req, res) => res.send('OK')); + +// Wrap the existing router +const typedRouter = wrapRouter(existingRouter, MyApi); + +// Now add typed routes using the wrapped router +// typedRouter.get('my.api.operation', ...); + +app.use('/api', typedRouter); // Mount the router (which is the original instance) +``` diff --git a/website/sidebars.js b/website/sidebars.js index 728b005c..b32a8f5a 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -92,6 +92,22 @@ const sidebars = { 'reference/superagent-wrapper/api-client', ], }, + { + type: 'category', + label: 'typed-express-router', + link: { + type: 'doc', + id: 'reference/typed-express-router/index', + }, + items: [ + 'reference/typed-express-router/create-router', + 'reference/typed-express-router/wrap-router', + 'reference/typed-express-router/typed-router', + 'reference/typed-express-router/request-response', + 'reference/typed-express-router/configuration', + 'reference/typed-express-router/typed-request-handler', + ], + }, ], }, ],