Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ts-oas/ts-oas

Automatically generate OpenAPI specifications from Typescript types.
https://github.com/ts-oas/ts-oas

json-schema oas openapi swagger typescript

Last synced: 3 months ago
JSON representation

Automatically generate OpenAPI specifications from Typescript types.

Awesome Lists containing this project

README

        

# Typescript OpenAPI Spec Generator

[![NPM version](https://img.shields.io/npm/v/ts-oas.svg)](https://www.npmjs.com/package/ts-oas)
![GitHub License](https://img.shields.io/github/license/ts-oas/ts-oas)
![NPM Unpacked Size](https://img.shields.io/npm/unpacked-size/ts-oas)

Automatically generate OpenAPI (formerly Swagger) specifications from Typescript types. Supports OpenAPI **v3.1** and **v3.0**. Requires interfaces/types in a specific format.

## Benefits

- **Write once, use many.** Typescript provides a fluent way to declare API specifications. With `ts-oas`, you can use the generated specs not only for documentation but also for input validation (eg. with AJV), serialization, maintaining business logic or test codes (with generics), and more.
- **Automation first.** Simply write a script to regenerate specs accordingly after any type changes.
- **Headless.** Works seamlessly with any server-side framework, unlike some other tools.

## Features

- Both [Programmatic](#a-quick-example) and [Command line](#cli) support.
- Reference schemas and components. Generate schema references that correspond to their Typescript type references.
- Supports JSDoc annotations. With both pre-defined and custom keywords, metadata can be included in every schema object.
- Schema processor function for any custom post-processing (if JSDoc annotations aren't enough).
- Generate schemas separately.
- Typescript 4 and 5 compliant.

## Install

```
npm i ts-oas
```

## Getting Started

Firstly, We need types for each API, compatible with the following format:

```ts
type Api = {
path: string;
method: HTTPMethod;
body?: Record;
params?: Record;
query?: Record;
responses: Partial>;
security?: Record[];
};
```

### A quick example

We have `interfaces.ts` where our API types are present:

```ts
import { ApiMapper } from "ts-oas"; // Recommended to use ApiMapper to help to keep the format valid.

export type GetBarAPI = ApiMapper<{
path: "/foo/bar/:id";
method: "GET";
params: {
id: number;
};
query: {
from_date: Date;
};
responses: {
/**
* @contentType application/json
*/
"200": Bar;
"404": { success: false };
};
}>;

/**
* Sample description.
* @summary Add a Bar
*/
export type AddBarAPI = ApiMapper<{
path: "/foo/bar";
method: "POST";
body: Bar;
responses: {
/**
* No content
*/
"201": never;
};
}>;

export type Bar = {
/**
* Description for barName.
* @minLength 10
*/
barName: string;
barType: "one" | "two";
};
```

In `script.ts` file:

```ts
import TypescriptOAS, { createProgram } from "ts-oas";
import { resolve } from "path";

// create a Typescript program. or any generic ts program can be used.
const tsProgram = createProgram(["interfaces.ts"], { strictNullChecks: true }, resolve());

// initiate the OAS generator.
const tsoas = new TypescriptOAS(tsProgram, { ref: true });

// get the complete OAS. determine type names (Regex/exact name) to be considered for specs.
const specObject = tsoas.getOpenApiSpec([/API$/]); // all types that ends with "API"

// log results:
console.log(JSON.stringify(specObject, null, 4));

// or write into a ts file:
// writeFileSync("./schema.ts", `const spec = ${inspect(specObject, { depth: null })};\n`);
```

Run the above script.

Expected output

```json
{
"openapi": "3.1.0",
"info": {
"title": "OpenAPI specification",
"version": "1.0.0"
},
"components": {
"schemas": {
"Bar": {
"type": "object",
"properties": {
"barName": {
"description": "Description for barName.",
"minLength": 10,
"type": "string"
},
"barType": {
"enum": ["one", "two"],
"type": "string"
}
},
"required": ["barName", "barType"]
}
}
},
"paths": {
"/foo/bar/:id": {
"get": {
"operationId": "GetBarAPI",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "number"
}
},
{
"name": "from_date",
"in": "query",
"required": true,
"schema": {
"type": "string",
"format": "date-time"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Bar"
}
}
}
},
"404": {
"description": "",
"content": {
"*/*": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"enum": [false]
}
},
"required": ["success"]
}
}
}
}
}
}
},
"/foo/bar": {
"post": {
"operationId": "AddBarAPI",
"description": "Sample description.",
"summary": "Add a Bar",
"requestBody": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/Bar"
}
}
}
},
"responses": {
"201": {
"description": "No content"
}
}
}
}
}
}
```

### Get schemas separately

Schemas with any format can be generated by:

```ts
const schema = tsoas.getSchemas(["Bar"]);
console.log(schema);
```

Expected output

```
{
Bar: {
type: 'object',
properties: {
barName: {
description: 'Description for barName.',
minLength: 10,
type: 'string'
},
barType: { enum: [ 'one', 'two' ], type: 'string' }
},
required: [ 'barName', 'barType' ]
}
}
```

## CLI

Command line tool is designed to behave just like the programmatic way. Once it has been installed, CLI can be executed using `npx ts-oas`, or just `ts-oas` if installed globally.

```
Usage: ts-oas [options]

: Comma-separated list of relative .ts file paths which contain
types.
: Comma-separated list of type names (Regex/exact name) to be
considered in files.

Options:
-c, --tsconfig-file Path to a JSON tsconfig file. [string]
-p, --options-file Path to a JSON file containing 'ts-oas' Options. Refer to
the documentation. [string]
-s, --spec-file Path to a JSON file containing additional OpenAPI
specifications. [string]
-e, --schema-only Only generates pure schemas from given types.
('spec-file' will be ignored.) [boolean]
-o, --output Path to a JSON file that will be used to write the
output. Will create the file if not existed. [string]
-h, --help Show help [boolean]
-v, --version Show version number [boolean]

Examples:
ts-oas ./interfaces/sample.ts myApi,mySecondApi

```

## Documentations

### JSDoc annotations

| Keyword | Fields | Examples |
| -------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| @default | any | `@default foo` `@default 3` `@default ["a", "b", "c"]` |
| @format | strings | `@format email` |
| @items | arrays | `@items.minimum 1` `@items.format email` `@items {"type":"integer", "minimum":0}` `@default ["a", "b", "c"]` |
| @$ref | any | `@ref http://my-schema.org` |
| @title | any | `@title foo` |
| @minimum
@maximum
@exclusiveMinimum
@exclusiveMaximum | numbers | `@minimum 10` `@maximum 100` |
| @minLength
@maxLength | strings | `@minLength 10` `@maxLength 100` |
| @minItems
@maxItems | arrays | `@minItems 10` `@maxItems 100` |
| @minProperties
@maxProperties | objects | `@minProperties 10` `@maxProperties 100` |
| @additionalProperties | objects | `@additionalProperties` |
| @ignore | any | `@ignore` |
| @pattern | strings | `@pattern /^[0-9a-z]+$/g` |
| @example | any | `@example foo` `@example {"abc":true}` `@example require('./examples.ts').myExampleConst` |
| @examples | any | `@example ["foo", "bar"]` `@example require('./examples.ts').myExampleArrayConst` |

#### Special keywords for root of API types

| @summary | @operationId | @tags | @ignore | @body.description | @body.contentType |
| -------- | ------------ | ----- | ------- | ----------------- | ----------------- |

Example

```ts
/**
* Sample description.
* @summary Summary of Endpoint
* @operationId addBar
* @tags foos,bars
* @ignore
* @body.description Description for body of request.
* @body.contentType application/json
*/
export type AddBarAPI = ApiMapper<{
path: "/foo/bar";
method: "POST";
body: Bar;
responses: {
"201": {};
};
}>;
```

#### Special keywords for response items

| @contentType |
| ------------ |

Example

```ts
...
responses: {
/**
* Description for response 200.
* @contentType application/json
*/
"200": { success: true };
};
```

### Options

#### `ref`

> _default: false_

Defines references for schemas based on their type references.

#### `titles`

> _default: false_

Provides a `title` field in each schema, filled with its corresponding field name or type name.

#### `ignoreRequired`

> _default: false_

Ignores the `required` field in all schemas.

#### `ignoreErrors`

> _default: false_

Ignores errors in Typescript files. May introduce wrong schemas.

#### `uniqueNames`

> _default: false_

Replaces every type name with a unique hash to avoid duplication issues.

#### `tsNodeRegister`

> _default: false_

Uses `ts-node/register` as a runtime argument, enabling direct execution of TypeScript on Node.js without precompiling.

#### `nullableKeyword`

> _default: true_

Provides `nullable: true` for nullable fields; otherwise, set `type: "null"`.

#### `defaultContentType`

> _default: "\*/\*"_

Sets the default content type for all operations. This can be overridden case-by-case (see the annotations section).

#### `defaultNumberType`

> _default: "number"_

Sets the default schema type for number values, which can be overridden case-by-case (see the annotations section).

#### `customKeywords`

A list of custom keywords to consider in annotations.

#### `customKeywordPrefix`

> _default: "x-"_

The prefix added to all `customKeywords`.

#### `schemaProcessor`

A function that runs over each generated schema.

## Inspirations

`ts-oas` is highly inspired by [typescript-json-schema](https://github.com/YousefED/typescript-json-schema). While using the so-called library, it took lots of workarounds to create compatible OpenAPI v3.0 specs. For example, modifying output schemas enforced us to use schema-walker tools which added lots of overhead in our scripts (Despite of compatible OpenAPI schemas in `ts-oas`, we added a schema-processor custom function as an option as well).

## Contributing

Contributions of any kind are welcome!

TODOs

- [x] CLI
- [ ] Support for request and response header specs
- [ ] More docs and examples
- [ ] Complete tests