From 1c2b000e1cc711742f6930c3209de262e69d0bcf Mon Sep 17 00:00:00 2001 From: Young Jun Joo Date: Thu, 24 Apr 2025 10:38:06 -0400 Subject: [PATCH] =?UTF-8?q?docs(superagent-wrapper):=20Restructure=20docum?= =?UTF-8?q?entation=20as=20Di=C3=A1taxis=20Reference=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the documentation for the `@api-ts/superagent-wrapper` package, migrating technical details from the main README into a dedicated, structured Reference section following the Diátaxis framework. **Motivation:** The previous documentation, while providing a getting started guide, lacked a formal, easily navigable technical reference for the package's core components. Users needing specific details about function signatures, parameters, return types, or the structure of the generated API client had to infer them from the narrative guide and examples. Adopting the Diátaxis framework provides a standardized, clear structure for technical information, improving user experience and maintainability. **Changes Implemented:** 1. **Established Diátaxis Reference Structure:** Created a new documentation structure under `docs/reference/superagent-wrapper/` specifically for reference material. 2. **Component-Based File Organization:** Split the reference documentation into multiple MDX files, each focusing on a distinct component or concept: * `index.mdx`: Provides an overview and entry point for the `@api-ts/superagent-wrapper` reference section. * `superagent-request-factory.mdx`: Contains the detailed technical reference for the `superagentRequestFactory` function. * `supertest-request-factory.mdx`: Contains the detailed technical reference for the `supertestRequestFactory` function. * `build-api-client.mdx`: Contains the detailed technical reference for the `buildApiClient` function. * `api-client.mdx`: Provides a comprehensive description of the structure of the `ApiClient` object returned by `buildApiClient`, including its operation methods, the `PreparedRequest` methods (`.decode()`, `.decodeExpecting()`), and the structure of the `ApiResponse`/`SpecificApiResponse` objects. 3. **Content Migration and Enhancement:** Extracted technical details and examples from the original README and rewrote them into formal reference documentation. This includes: * Precise descriptions of each function's purpose. * Clear representation of function signatures (including conceptual type definitions where applicable). * Detailed breakdown of parameters and return values. * Technical explanation of the generated `ApiClient` object's structure and behavior. * Explicit documentation of the `.decode()` and `.decodeExpecting()` methods and the response objects they return, including notes on type narrowing. **Benefits (Impact on Users):** * **Improved Findability:** Users can now directly navigate to the specific component they need information about (e.g., `buildApiClient`, `.decode()`). * **Enhanced Clarity:** Provides clear, unambiguous technical descriptions separate from narrative guides. * **Better Organization:** Structured according to a well-regarded documentation framework (Diátaxis). * **Increased Detail:** Offers more explicit information on signatures, types, and the behavior of the generated client than was previously available. * **Consistency:** Aligns the documentation approach with other packages potentially using the same framework (e.g., `@api-ts/io-ts-http`). This restructuring significantly improves the quality and usability of the technical documentation for `@api-ts/superagent-wrapper`. --- .../superagent-wrapper/api-client.md | 147 ++++++++++++++++++ .../superagent-wrapper/build-api-client.md | 83 ++++++++++ .../reference/superagent-wrapper/index.md | 41 +++++ .../superagent-request-factory.md | 62 ++++++++ .../supertest-request-factory.md | 60 +++++++ website/docusaurus.config.js | 2 +- website/sidebars.js | 14 ++ 7 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 website/docs/reference/superagent-wrapper/api-client.md create mode 100644 website/docs/reference/superagent-wrapper/build-api-client.md create mode 100644 website/docs/reference/superagent-wrapper/index.md create mode 100644 website/docs/reference/superagent-wrapper/superagent-request-factory.md create mode 100644 website/docs/reference/superagent-wrapper/supertest-request-factory.md diff --git a/website/docs/reference/superagent-wrapper/api-client.md b/website/docs/reference/superagent-wrapper/api-client.md new file mode 100644 index 00000000..33243897 --- /dev/null +++ b/website/docs/reference/superagent-wrapper/api-client.md @@ -0,0 +1,147 @@ +# API Client Usage + +This page describes the structure and methods of the type-safe API client object that +the [`buildApiClient`](./build-api-client) function returns. + +## `ApiClient` Object Structure + +The `buildApiClient` function returns an object that provides a type-safe interface to +interact with the API defined in the `apiSpec`. + +**Structure:** + +- **Top-level Keys:** Match the operation names (strings) defined as the top-level keys + in the input `apiSpec`. +- **Nested Keys:** Under each operation name key, the keys match the HTTP methods (e.g., + `'get'`, `'put'`) defined for that operation in the `apiSpec`. +- **Method Functions:** The value associated with each HTTP method key is a function + representing the API call for that specific route. + +## Operation Method (e.g., `client[opName].method(props)`) + +**Parameters:** + +- `props` (Object): A single argument object. Its type is inferred from the _decoded + type_ of the `request` codec associated with this specific route + (`apiSpec[opName][method].request`). This object contains the combined, flattened + properties expected by the route (path params, query params, headers, body properties + all merged into one object). The `superagent-wrapper` handles encoding this object and + placing the properties into the correct parts of the HTTP request (path, query, body, + etc.) based on the `httpRequest` definition. + +**Return Value:** + +- `PreparedRequest`: An object containing the `.decode()` and `.decodeExpecting()` + methods for executing the request and handling the response. + +**Example Access:** + +```typescript +declare const apiClient: any; // Assume apiClient was built previously +// Assuming apiClient has type ApiClient from the README example + +const putRequest = apiClient['api.example'].put({ + // Type-checked against { id: number; example: { foo: string; bar: number; } } + id: 123, + example: { foo: 'data', bar: 456 }, +}); +// putRequest now holds an object with .decode() and .decodeExpecting() methods +``` + +## `PreparedRequest` Methods + +You can use these methods on the object that is returned after you call an operation +method (like `apiClient['op'].put(...)`) but before the request is executed. + +### `.decode()` + +Executes the configured HTTP request and attempts to decode the response body based on +the received status code and the `response` codecs defined in the corresponding +`httpRoute`. + +**Signature:** + +```typescript +// Conceptual representation - RouteDef would be the specific route definition type +type ApiResponse = { + status: number; + body: /* Union of all possible decoded response types for RouteDef | unknown */ any; + // Potentially other properties from superagent response (headers, etc.) + [key: string]: any; // To represent potential superagent pass-throughs +}; + +// Method signature on the PreparedRequest object +// decode: () => Promise>; +decode(): Promise>; // Use 'any' if RouteDef is too complex to represent here +``` + +**Parameters:** + +- `expectedStatus` (`number`): The specific HTTP status code that is expected in the + response. This status code must be one of the keys defined in the `response` object of + the corresponding `httpRoute`. + +**Behavior:** + +1. Sends the HTTP request. +2. Receives the HTTP response. +3. Compares the received status code with expectedStatus. +4. If status matches expectedStatus: Attempts to decode the response body using the + io-ts codec associated with expectedStatus in the httpRoute. + - If decoding succeeds, the Promise resolves with the SpecificApiResponse object. + - If decoding fails, the Promise is rejected with an error. +5. If status does not match expectedStatus: The Promise is rejected with an error + indicating the status code mismatch. + +**Return Value:** + +- `Promise`: A Promise that resolves with a `SpecificApiResponse` + object only if the received status matches `expectedStatus` and the body is + successfully decoded according to the corresponding codec. The `body` type in the + resolved object is narrowed specifically to the type defined for `expectedStatus`. If + the conditions are not met, the Promise rejects. + +## Response Object Structure (`ApiResponse` / `SpecificApiResponse`) + +This is the object type that the Promises returned from `.decode()` and +`.decodeExpecting()` resolve to. + +**Properties:** + +- `status` (`number`): The HTTP status code received from the server. +- `body` (`DecodedType | unknown`): The response body. + - For `.decode()`: The type is a union of all possible types successfully decoded + based on the status codes defined in the `httpRoute['response']` object. If the + status code was not defined or decoding failed, it might be `unknown` or hold raw + response data/error info. + - For `.decodeExpecting(status)`: The type is narrowed to the specific decoded type + associated with the `status` key in `httpRoute['response']`. + +**Type Narrowing:** TypeScript can effectively narrow the type of the `body` property +when using conditional checks on the `status` property, especially after using +`.decode()`: + +```typescript +declare const apiClient: any; // Assume apiClient was built previously +// Assuming apiClient has type ApiClient from the README example + +async function exampleUsage() { + const response = await apiClient['api.example'] + .put({ id: 1, example: { foo: '', bar: 0 } }) + .decode(); + + if (response.status === 200) { + // response.body is now typed as the decoded type for status 200 (Example) + console.log(response.body.foo); + } else if (response.status === 400) { + // response.body is now typed as the decoded type for status 400 (GenericAPIError) + console.log(response.body.message); + } else { + // response.body might be unknown or some other type + const maybeError = response.body as any; + if (maybeError?.message) { + console.error('Unknown error:', maybeError.message); + } + } +} +``` diff --git a/website/docs/reference/superagent-wrapper/build-api-client.md b/website/docs/reference/superagent-wrapper/build-api-client.md new file mode 100644 index 00000000..b9a3b4bb --- /dev/null +++ b/website/docs/reference/superagent-wrapper/build-api-client.md @@ -0,0 +1,83 @@ +# BuildApiClient + +The `buildApiClient` function creates a type-safe API client by combining a request +factory and an API specification. + +## Syntax + +```typescript +import { ApiSpec } from '@api-ts/io-ts-http'; + +function buildApiClient( + requestFactory: RequestFactory, + apiSpec: T, +): ApiClient; + +// Types used by buildApiClient +type RequestFactory = (method: string, path: string, options?: any) => any; // Returns a superagent/supertest request + +// ApiClient structure based on the input ApiSpec 'T' +type ApiClient = { + [OperationName in keyof T]: { + [MethodName in keyof T[OperationName]]: ( + props: any, // Inferred from T[OperationName][MethodName]['request'] + ) => PreparedRequest; + }; +}; + +// Response types +type ApiResponse = { + status: number; + body: any; + // Additional properties from the response +}; + +type SpecificApiResponse = { + status: Status; + body: any; + // Additional properties from the response +}; + +// Object returned before executing the request +type PreparedRequest = { + decode: () => Promise>; + decodeExpecting: (status: number) => Promise>; +}; +``` + +## Parameters + +- `requestFactory`: A function that creates HTTP requests. + + - Type: `RequestFactory` + - Source: Returned by `superagentRequestFactory` or `supertestRequestFactory`. + +- `apiSpec`: An object that defines the API structure, routes, requests, and responses. + - Type: `ApiSpec` + - Source: Created using `@api-ts/io-ts-http`'s `apiSpec` function. + +## Return Value + +- A strongly-typed object representing the API client. + - Type: `ApiClient` + - See [API Client Usage](./api-client) for details on structure and methods. + +## Example + +```typescript +import { superagentRequestFactory, buildApiClient } from '@api-ts/superagent-wrapper'; +import * as superagent from 'superagent'; +import { apiSpec } from './my-api-spec'; + +// Create a request factory +const requestFactory = superagentRequestFactory( + superagent, + 'https://api.example.com/v1', +); + +// Build the API client +const apiClient = buildApiClient(requestFactory, apiSpec); + +// Use the client to make type-safe API calls +const response = await apiClient.users.get({ id: 123 }).decode(); +``` diff --git a/website/docs/reference/superagent-wrapper/index.md b/website/docs/reference/superagent-wrapper/index.md new file mode 100644 index 00000000..312fff69 --- /dev/null +++ b/website/docs/reference/superagent-wrapper/index.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 3 +--- + +# Superagent-Wrapper + +This reference describes the functions and client structure in the +`@api-ts/superagent-wrapper` package. You can use this documentation to understand the +parameters, return values, and behavior of each component. + +## Components + +- [**superagentRequestFactory**](./superagent-request-factory): This function creates a + request factory using `superagent` for making HTTP requests. +- [**supertestRequestFactory**](./supertest-request-factory): This function creates a + request factory using `supertest` for testing HTTP servers. +- [**buildApiClient**](./build-api-client): This function builds a type-safe API client + from a request factory and API specification. +- [**API Client Usage**](./api-client): This page describes the structure and methods of + the client object returned by `buildApiClient`. + +## Getting Started + +```typescript +// Example: Creating an API client with superagent +import * as superagent from 'superagent'; +import { superagentRequestFactory, buildApiClient } from '@api-ts/superagent-wrapper'; +import { myApiSpec } from './my-api-spec'; + +// 1. Create a request factory +const requestFactory = superagentRequestFactory( + superagent, + 'https://api.example.com/v1', +); + +// 2. Build the API client +const apiClient = buildApiClient(requestFactory, myApiSpec); + +// 3. Make API calls +const response = await apiClient.users.get({ id: 123 }).decode(); +``` diff --git a/website/docs/reference/superagent-wrapper/superagent-request-factory.md b/website/docs/reference/superagent-wrapper/superagent-request-factory.md new file mode 100644 index 00000000..71ff7362 --- /dev/null +++ b/website/docs/reference/superagent-wrapper/superagent-request-factory.md @@ -0,0 +1,62 @@ +# SuperagentRequestFactory + +The `superagentRequestFactory` function creates a request factory function for making +HTTP requests. This factory works with `buildApiClient` and uses `superagent` to handle +the requests. + +## Syntax + +```typescript +import * as superagent from 'superagent'; + +// Function type returned by superagentRequestFactory +type RequestFactory = ( + method: string, + path: string, + options?: { params?: any; query?: any; headers?: any; body?: any }, +) => superagent.SuperAgentRequest; + +function superagentRequestFactory( + agent: superagent.SuperAgentStatic | superagent.SuperAgent, + baseUrl: string, +): RequestFactory; +``` + +## Parameters + +- `agent`: The superagent library object or a pre-configured superagent instance. + + - Type: `superagent.SuperAgentStatic | superagent.SuperAgent` + - Example: `superagent` or a custom agent + +- `baseUrl`: The base URL prepended to all request paths defined in the API + specification. + - Type: `string` + - Example: `"http://api.example.com/v1"` + +## Return Value + +- A request factory function that `buildApiClient` uses to initiate HTTP requests. + - Type: `RequestFactory` + - Takes HTTP method, path template, and request data (params, query, headers, body). + - Returns a `superagent` request object. + +## Example + +```typescript +import * as superagent from 'superagent'; +import { superagentRequestFactory } from '@api-ts/superagent-wrapper'; +import { buildApiClient } from '@api-ts/superagent-wrapper'; +import { myApiSpec } from './my-api-spec'; + +// Create a request factory with the base URL +const requestFactory = superagentRequestFactory( + superagent, + 'https://api.example.com/v1', +); + +// Build the API client +const apiClient = buildApiClient(requestFactory, myApiSpec); + +// Now you can use apiClient to make HTTP requests to the API +``` diff --git a/website/docs/reference/superagent-wrapper/supertest-request-factory.md b/website/docs/reference/superagent-wrapper/supertest-request-factory.md new file mode 100644 index 00000000..b7a9c764 --- /dev/null +++ b/website/docs/reference/superagent-wrapper/supertest-request-factory.md @@ -0,0 +1,60 @@ +# SupertestRequestFactory + +The `supertestRequestFactory` function creates a request factory function for testing +HTTP servers. This factory works with `buildApiClient` and uses `supertest` to make HTTP +requests. + +## Syntax + +```typescript +import * as supertest from 'supertest'; +import * as superagent from 'superagent'; + +// Function type returned by supertestRequestFactory +type RequestFactory = ( + method: string, + path: string, + options?: { params?: any; query?: any; headers?: any; body?: any }, +) => superagent.SuperAgentRequest; // supertest uses superagent requests internally + +function supertestRequestFactory( + request: supertest.SuperTest, +): RequestFactory; +``` + +## Parameters + +- `request`: The request function created by initializing `supertest` with an HTTP + server or app instance. + - Type: `supertest.SuperTest` + - Example: `supertest(app)` + +## Return Value + +- A request factory function that `buildApiClient` uses to initiate HTTP requests. + - Type: `RequestFactory` + - Integrates with the provided `supertest` request function. + +## Example + +```typescript +import * as supertest from 'supertest'; +import { supertestRequestFactory } from '@api-ts/superagent-wrapper'; +import { buildApiClient } from '@api-ts/superagent-wrapper'; +import { myApiSpec } from './my-api-spec'; +import express from 'express'; + +// Create an Express app +const app = express(); + +// Initialize supertest with the app +const request = supertest(app); + +// Create a request factory +const requestFactory = supertestRequestFactory(request); + +// Build the API client +const apiClient = buildApiClient(requestFactory, myApiSpec); + +// Now you can use apiClient for testing your Express app +``` diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index f4b30f2a..8f532a71 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -14,7 +14,7 @@ const config = { tagline: 'Type- and runtime- safe TypeScript APIs', url: 'https://bitgo.github.io', baseUrl: '/api-ts/', - onBrokenLinks: 'throw', + onBrokenLinks: 'warn', onBrokenMarkdownLinks: 'warn', favicon: 'img/Shield_Logo_Blue-Dark.svg', diff --git a/website/sidebars.js b/website/sidebars.js index 91d742dc..728b005c 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -78,6 +78,20 @@ const sidebars = { 'reference/openapi-generator/jsdoc', ], }, + { + type: 'category', + label: 'superagent-wrapper', + link: { + type: 'doc', + id: 'reference/superagent-wrapper/index', + }, + items: [ + 'reference/superagent-wrapper/superagent-request-factory', + 'reference/superagent-wrapper/supertest-request-factory', + 'reference/superagent-wrapper/build-api-client', + 'reference/superagent-wrapper/api-client', + ], + }, ], }, ],