npm package: @map-colonies/openapi-helpers
Description: A package that provides utilities for working with openapi files
Latest version: 5.1.0

# openapi-helpers
This package contains scripts, types and functions to help you work with OpenAPI.


## Installation
Run the following commands:

```bash
npm install --save-dev @map-colonies/openapi-helpers supertest prettier openapi-typescript @types/express
```


## CLI Usage

The package provides a unified CLI for generating TypeScript types and error classes from OpenAPI specifications. All code generation is now performed using the `generate` command, which supports subcommands for types and errors.

#### CLI Arguments Reference

**Positional Arguments:**
For both `generate types` and `generate errors` commands, the positional arguments are:

- `<openapi-file>`: Path to the OpenAPI YAML or JSON file to use as the source schema.
- `<output-file>`: Path to the file where the generated code will be written.

These arguments are required and must be provided in the order shown.

**Optional Arguments:**

For `generate types`:
- `-f, --format`: Format the generated types using Prettier
- `-t, --add-typed-request-handler`: Add the TypedRequestHandler type to the generated types

For `generate errors`:
- `-f, --format`: Format the generated code using Prettier  
- `-e, --errors-output <all|map|classes>`: Specify what to generate (default: all)
  - `all`: generate both error classes and error code mapping
  - `map`: generate only the error code mapping
  - `classes`: generate only the error classes

### Generate Types

Generate TypeScript types from an OpenAPI schema:
```bash
npx @map-colonies/openapi-helpers generate types <openapi-file> <output-file> [options]
```

For example:
```bash
npx @map-colonies/openapi-helpers generate types ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler
```

Options:
- `-f, --format` - Format the generated types using `prettier`.
- `-t, --add-typed-request-handler` - Add the `TypedRequestHandler` type to the generated types.

### Generate Errors

Generate error classes and error code mappings from an OpenAPI schema:
```bash
npx @map-colonies/openapi-helpers generate errors <openapi-file> <output-file> [options]
```

For example:
```bash
npx @map-colonies/openapi-helpers generate errors ./openapi3.yaml ./src/errors.ts --format
```


Options:
- `-f, --format` - Format the generated code using `prettier`.
- `-e, --errors-output <all|map|classes>` - Specify what to generate:
  - `all` (default): generate both error classes and error code mapping
  - `map`: generate only the error code mapping
  - `classes`: generate only the error classes

### Help and Examples

To see all available commands and options:
```bash
npx @map-colonies/openapi-helpers --help
npx @map-colonies/openapi-helpers generate --help
npx @map-colonies/openapi-helpers generate types --help
npx @map-colonies/openapi-helpers generate errors --help
```


#### Example: Run all generations

You can run both types and errors generation in sequence:
```bash
npx @map-colonies/openapi-helpers generate types ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler
npx @map-colonies/openapi-helpers generate errors ./openapi3.yaml ./src/errors.ts --format --errors-output all
```



## Programmatic Support


> [!NOTE]
> **Programmatic usage of the CLI (importing and using the generators directly) is only supported in ECMAScript modules (ESM).** CommonJS is not supported for direct imports.

The code generators (`generateTypes.mts` and `generateErrors.mts`) now support functional programming patterns. You can inject custom transformation logic or AST manipulation by providing functional arguments, making the generators more flexible and composable for advanced use cases.


### API Usage

You can import and use the generators directly in your own scripts for full functional programming flexibility:

```typescript
import { generateTypes, generateErrors } from '@map-colonies/openapi-helpers/generators';

// Generate types
await generateTypes(
  'openapi3.yaml',
  'src/openapi.d.ts',
  {
    shouldFormat: true,
    addTypedRequestHandler: true,
    // inject?: string,
    // transform?: (schemaObject, metadata) => ...
  }
);

// Generate errors
await generateErrors(
  'openapi3.yaml',
  'src/errors.ts',
  {
    shouldFormat: true,
    includeMapping: true,
    includeErrorClasses: true
  }
);
```

You can pass custom `inject` or `transform` functions to `generateTypes` for advanced AST/code manipulation, enabling highly composable and functional workflows.

## TypedRequestHandler
The package contains a wrapper for the `express` types package that provides autocomplete for all the request handlers to the API based on the OpenAPI schema. The TypedRequestHandler is initialized with the types generated by `openapi-typescript`, and is configured based on operation name or method and path.

### Usage
```typescript
import { TypedRequestHandlers } from '@map-colonies/openapi-helpers/typedRequestHandler';
import type { paths, operations } from './src/openapi.d.ts';

// Initialize the TypedRequestHandlers with the paths and operations types
// This can be done in a separate file and exported, in the same file or even in the same line
type MyHandlers = TypedRequestHandlers<paths, operations>;

export class Controller {
  // Define the handler for the operation based on method and path
  public getResource: MyHandlers['GET /resource'] = (req, res) => {
    res.status(httpStatus.OK).json({id: 1, description: 'description', name: 'name'});
  };


  // Define the handler for the operation based on the operation name
  public getResource: MyHandlers['getResource'] = (req, res) => {
    res.status(httpStatus.OK).json({id: 1, description: 'description', name: 'name'});
  };
}
```

## RequestSender
The package contains a wrapper for the `supertest` package that provides autocomplete for all the requests to the API based on the OpenAPI schema. The RequestSender is initialized with the server's base URL and the OpenAPI schema and the types exported by `openapi-typescript`.

```typescript
import { RequestSender } from '@map-colonies/openapi-helpers/requestSender';
import type { paths, operations } from './src/openapi.d.ts';

const requestSender = await createRequestSender<paths, operations>('path/to/openapi3.yaml', expressApp);
```

The requestSender object contains all the paths and operations defined in the OpenAPI schema. For example, to send a request to the `getUsers` operation with the `/users` path and with the `GET` method, you can use the following code:

```typescript
const response = await requestSender.getUsers();

// or

const response = await requestSender.sendRequest({ 
  method: 'get', 
  path: '/simple-request'
});
```

The package supports all the operations defined in the OpenAPI schema, either by operation name, or by using the `sendRequest` function with the method, path and parameters.

### Response Status Assertion

The `expectResponseStatusFactory` provides TypeScript type narrowing for response status assertions in tests. This utility creates an assertion function that narrows the response type based on the expected status code, giving you full type safety when working with different response types.

#### Usage

```typescript
import { expectResponseStatusFactory } from '@map-colonies/openapi-helpers/requestSender';
import type { ExpectResponseStatus } from '@map-colonies/openapi-helpers/requestSender';
import { describe, it, expect } from 'vitest';

// Create the assertion function with the exported type
const expectResponseStatus: ExpectResponseStatus = expectResponseStatusFactory(expect);

it('should return user data on success', async () => {
  const response = await requestSender.getUser({ pathParams: { id: '123' } });
  
  // Assert the status and narrow the type
  expectResponseStatus(response, 200);
  
  // TypeScript now knows response is the 200 response type
  // You can safely access response.body with full autocomplete
  console.log(response.body.id);
  console.log(response.body.name);
});

it('should handle error responses', async () => {
  const response = await requestSender.getUser({ pathParams: { id: 'invalid' } });
  
  if (response.status === 404) {
    expectResponseStatus(response, 404);
    // TypeScript knows this is a 404 response
    console.log(response.body.error);
  }
});
```

> [!TIP]
> **Avoiding TS2775 Error**: When using `expectResponseStatusFactory`, you must provide an explicit type annotation using the exported `ExpectResponseStatus` type as shown above. This is required by TypeScript's strict mode for assertion functions.

#### Reusable Setup

To avoid repeating the setup in every test file, create a shared test utility:

```typescript
// test/utils/assertions.ts
import { expectResponseStatusFactory } from '@map-colonies/openapi-helpers/requestSender';
import type { ExpectResponseStatus } from '@map-colonies/openapi-helpers/requestSender';
import { expect } from 'vitest';

export const expectResponseStatus: ExpectResponseStatus = expectResponseStatusFactory(expect);
```

Then import and use it in your tests:

```typescript
// test/api.spec.ts
import { expectResponseStatus } from './utils/assertions';

it('should work', async () => {
  const response = await requestSender.getUser({ pathParams: { id: '123' } });
  expectResponseStatus(response, 200);
  // TypeScript knows the exact response type
});
```

#### How it works

The `expectResponseStatusFactory` takes an expect function (from your test framework) and returns a function that:
1. Asserts that the response status matches the expected status
2. Narrows the TypeScript type of the response to only include the matching status code

This is particularly useful when working with OpenAPI-generated types that have multiple possible response types (e.g., 200 success, 404 not found, 500 error). After calling `expectResponseStatus`, TypeScript will know exactly which response type you're working with.

#### Benefits

- **Type Safety**: Full TypeScript support with automatic type narrowing
- **Autocomplete**: Get accurate autocomplete for response body based on the status code
- **Test Clarity**: Combine assertion and type narrowing in a single function call
- **Prevents Errors**: Catch type mismatches at compile time instead of runtime


> [!IMPORTANT]
> For the package to function properly, you need to make sure that the following values are configured in your `tsconfig.json` or `jsconfig.json` files under compilerOptions:
> - module: "NodeNext"
> - moduleResolution: "NodeNext"