Skip to content

Commit

Permalink
docs: update docs on custom codecs in the generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ad-world committed Aug 8, 2024
1 parent b52f4ed commit 110d351
Showing 1 changed file with 120 additions and 30 deletions.
150 changes: 120 additions & 30 deletions packages/openapi-generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ API specification into an OpenAPI specification.

## Install

```
```shell
npm install --save-dev @api-ts/openapi-generator
```

Expand All @@ -15,7 +15,7 @@ The **openapi-generator** assumes the io-ts-http `apiSpec` is exported in the to
of the Typescript file passed as an input parameter. The OpenAPI specification will be
written to stdout.

```
```shell
ARGUMENTS:
<file> - API route definition file

Expand All @@ -35,32 +35,122 @@ For example:
npx openapi-generator src/index.ts
```

## Custom codec file

`openapi-generator` only reads files in the specified package, and stops at the module
boundary. This allows it to work even without `node_modules` installed. It has built-in
support for `io-ts`, `io-ts-types`, and `@api-ts/io-ts-http` imports. If your package
imports codecs from another external library, then you will have to define them in a
custom configuration file so that `openapi-generator` will understand them. To do so,
create a JS file with the following format:

```typescript
module.exports = (E) => {
return {
'io-ts-bigint': {
BigIntFromString: () => E.right({ type: 'string' }),
NonZeroBigInt: () => E.right({ type: 'number' }),
NonZeroBigIntFromString: () => E.right({ type: 'string' }),
NegativeBigIntFromString: () => E.right({ type: 'string' }),
NonNegativeBigIntFromString: () => E.right({ type: 'string' }),
PositiveBigIntFromString: () => E.right({ type: 'string' }),
},
// ... and so on for other packages
};
};
```
## Preparing a types package for reusable codecs

In order to use types from external `io-ts` types packages, you must ensure two things
are done.

1. The package source code must be included in the bundle, as the generator is built to
generate specs based from the Typescript AST. It is not set up to work with
transpiled js code. You can do this by modifying your `package.json` to include your
source code in the bundle. For example, if the source code is present in the `src/`
directory, then add `src/` to the files array in the `package.json` of your project.
2. After Step 1, change the `types` field in the `package.json` to be the entry point of
the types in the source code. For example, if the entrypoint is `src/index.ts`, then
set `"types": "src/index.ts"` in the `package.json`

## Defining Custom Codecs

When working with `openapi-generator`, you may encounter challenges with handling custom
codecs that require JavaScript interpretation or aren't natively supported by the
generator. These issues typically arise with codecs such as `new t.Type(...)` and other
primitives that aren't directly supported. However, there are two solutions to address
these challenges effectively. Click [here](#list-of-supported-io-ts-primitives) for the
list of supported primitives.

### Solution 1: Using a Custom Codec Configuration File

`openapi-generator` supports importing codecs from other packages in `node_modules`, but
it struggles with primitives that need JavaScript interpretation, such as
`new t.Type(...)`. To work around this, you can define schemas for these codecs in a
configuration file within your downstream types package (where you generate the API
docs). This allows the generator to understand and use these schemas where necessary.
Follow these steps to create and use a custom codec configuration file:

1. Create a JavaScript file with the following format:

```javascript
module.exports = (E) => {
return {
'io-ts-bigint': {
BigIntFromString: () => E.right({ type: 'string' }),
NonZeroBigInt: () => E.right({ type: 'number' }),
NonZeroBigIntFromString: () => E.right({ type: 'string' }),
NegativeBigIntFromString: () => E.right({ type: 'string' }),
NonNegativeBigIntFromString: () => E.right({ type: 'string' }),
PositiveBigIntFromString: () => E.right({ type: 'string' }),
},
// ... and so on for other packages
};
};
```

2. The input parameter `E` is the namespace import of `fp-ts/Either`, which avoids
issues with `require`. The return type should be a `Record` containing AST
definitions for external libraries. For more information on the structure, refer to
[KNOWN_IMPORTS](./src/knownImports.ts).

### Solution 2: Defining Custom Codec Schemas in the Types Package (recommended)

`openapi-generator` now offers the ability to define the schema of custom codecs
directly within the types package that defines them, rather than the downstream package
that uses them. This approach is particularly useful for codecs that are used in many
different types packages. Here’s how you can define schemas for your custom codecs in
the upstream repository:

1. Create a file named `openapi-gen.config.js` in the root of your repository.

2. Add the following line to the `package.json` of the types package:

```json
"customCodecFile": "openapi-gen.config.js"
```

3. In the `openapi-gen.config.js` file, define your custom codecs:

```javascript
module.exports = (E) => {
return {
SampleCodecDefinition: () =>
E.right({
type: 'string',
default: 'defaultString',
minLength: 1,
}),
// ... rest of your custom codec definitions
};
};
```

By following these steps, the schemas for your custom codecs will be included in the
generated API docs for any endpoints that use the respective codecs. The input parameter
`E` is the namespace import of `fp-ts/Either`, and the return type should be a `Record`
containing AST definitions for external libraries. For more details, see
[KNOWN_IMPORTS](./src/knownImports.ts).

## List of supported io-ts primitives

The input parameter `E` is the namespace import of `fp-ts/Either` (so that trying to
`require` it from the config file isn't an issue), and the return type is a `Record`
containing AST definitions for external libraries.
[Refer to KNOWN_IMPORTS here for info on the structure](./src/knownImports.ts)
- string
- number
- bigint
- boolean
- null
- nullType
- undefined
- unknown
- any
- array
- readonlyArray
- object
- type
- partial
- exact
- strict
- record
- union
- intersection
- literal
- keyof
- brand
- UnknownRecord
- void

0 comments on commit 110d351

Please sign in to comment.