Automatically generate OpenAPI (formerly Swagger) specifications from Typescript types. Supports OpenAPI v3.1 and v3.0. Requires interfaces/types in a specific format.
-
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.
- Both Programmatic and Command line 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.
npm i ts-oas
Firstly, We need types for each API, compatible with the following format:
type Api = {
path: string;
method: HTTPMethod;
body?: Record<string, any>;
params?: Record<string, any>;
query?: Record<string, any>;
responses: Partial<Record<HttpStatusCode, any>>;
security?: Record<string, string[]>[];
};
We have interfaces.ts
where our API types are present:
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:
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
{
"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"
}
}
}
}
}
}
Schemas with any format can be generated by:
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' ]
}
}
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 <file-paths> <type-names> [options]
<file-paths> : Comma-separated list of relative .ts file paths which contain
types.
<type-names> : 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
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
|
@summary | @operationId | @tags | @ignore | @body.description | @body.contentType |
---|
Example
/**
* 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": {};
};
}>;
@contentType |
---|
Example
...
responses: {
/**
* Description for response 200.
* @contentType application/json
*/
"200": { success: true };
};
default: false
Defines references for schemas based on their type references.
default: false
Provides a title
field in each schema, filled with its corresponding field name or type name.
default: false
Ignores the required
field in all schemas.
default: false
Ignores errors in Typescript files. May introduce wrong schemas.
default: false
Replaces every type name with a unique hash to avoid duplication issues.
default: false
Uses ts-node/register
as a runtime argument, enabling direct execution of TypeScript on Node.js without precompiling.
default: true
Provides nullable: true
for nullable fields; otherwise, set type: "null"
.
default: "*/*"
Sets the default content type for all operations. This can be overridden case-by-case (see the annotations section).
default: "number"
Sets the default schema type for number values, which can be overridden case-by-case (see the annotations section).
A list of custom keywords to consider in annotations.
default: "x-"
The prefix added to all customKeywords
.
A function that runs over each generated schema.
ts-oas
is highly inspired by 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).
Contributions of any kind are welcome!
TODOs
- [x] CLI
- [ ] Support for request and response header specs
- [ ] More docs and examples
- [ ] Complete tests