Awesome
Deepkit OpenAPI
<br /> <p align="center"> <img src="packages/docs/logo.svg" /> </p> <p align="center"> <i>from types, to types</i> </p>Introduction
:warning: THIS LIBRARY IS UNDER 1.0.0 :warning:
This is a Deepkit Framework module for automatically generating OpenAPI V3 definitions.
If you wonder what Deepkit is, it is a TypeScript framework that adds reflection functionalities to the already great TS language. With Deepkit, the user exploits the richness of TypeScript syntax to define solid API interface, painless validation and runtime self checks. Learn more at its official site.
OpenAPI, on the other hand, is a popular schema for HTTP API definitions. There are countless serverside frameworks supporting it, like Java Spring, Django, FastAPI and NestJS, to name a few. However, it is Deepkit who has the potential to map code to OpenAPI definition with the least repeated code. Deepkit provides the ability to have TypeScript's static types in runtime, therefore no more decorators like @ApiProperty
are needed. Also, Deepkit inherits the rich and rigid type system of TypeScript, with a battle-tested type checker tsc
and the wide typed JavaScript ecosystem, so it should provide more type safety than Python 3.5+.
Get Started
Warning: This package is intended to only work with deepkit
Install Deepkit Openapi
npm
npm install deepkit-openapi
yarn
yarn add deepkit-openapi
pnpm
pnpm add deepkit-openapi
Import OpenAPIModule
import { FrameworkModule } from "@deepkit/framework";
import { OpenAPIModule } from "deepkit-openapi";
// ...
@http.controller()
class UserController {
// ...
}
new App({
providers: [UserController],
controllers: [UserController],
imports: [
new OpenAPIModule({ prefix: "/openapi/" }), // Import the module here
new FrameworkModule({
publicDir: "public",
httpLog: true,
migrateOnStartup: true,
}),
],
})
.loadConfigFromEnv()
.run();
Start the Application
ts-node app.ts server:start
2022-06-04T15:14:57.744Z [LOG] Start server ...
2022-06-04T15:14:57.747Z [LOG] 4 HTTP routes
2022-06-04T15:14:57.747Z [LOG] HTTP Controller UserController
2022-06-04T15:14:57.747Z [LOG] GET /user/:id
2022-06-04T15:14:57.747Z [LOG] POST /user/
2022-06-04T15:14:57.747Z [LOG] PATCH /user/:id
2022-06-04T15:14:57.747Z [LOG] DELETE /user/:id
2022-06-04T15:14:57.747Z [LOG] HTTP listening at http://0.0.0.0:8080
2022-06-04T15:14:57.747Z [LOG] Server started.
Visit http://localhost:8080/openapi/index.html
Now you get your OpenAPI documentation up and running with a single line of code! No annotations needed!
new OpenAPIModule({ prefix: "/openapi/" })
Tutorial
Introducing OpenAPIModule
To use deepkit-openapi
, first we need to import OpenAPIModule
from deepkit-openapi
. OpenAPIModule
is an AppModule
that works like a custom deepkit module.
import { OpenAPIModule } from "deepkit-openapi";
To make this module effective, add it to your App
's import:
new App({
imports: [
new OpenAPIModule(),
new FrameworkModule(),
],
}).run();
The OpenAPIModule
does two things:
- Serve
openapi.yml
,openapi.json
under the root path, by default/openapi
. When the query comes, it builds the OpenAPI document on the fly, according to current working HTTP controllers. - Serve the Swagger UI static site at
index.html
, which loads theopenapi.yml
.
Basically, OpenAPiModule
builds the OpenAPI document lazily, according to the running controllers. Furthur works should be allowing the user to cache the generated document. It also serves Swagger UI, which includes exactly the files of swagger-ui-dist
, for your convenience. You can use your own OpenAPI loader, of course.
To customize OpenAPIModule
, provide a OpenAPIConfig
as the parameter of new OpenAPIModule()
. You can provide your own values optionally to it, allowing you to customize its functions.
import { OpenAPIModule, OpenAPIConfig } from 'deepkit-openapi';
new OpenAPIModule({
title: 'The title of your APIs', // default "OpenAPI"
description: 'The description of your APIs', // default ""
version: 'x.y.z', // The version of your APIs. default "1.0.0"
prefix: '/url-prefix', // The prefix of all OpenAPIModule controllers.
});
To further configure OpenAPIModule
, call the following chainable methods of OpenAPIModule
:
configureOpenApi(c: (openApi: OpenAPI) => void): OpenAPIModule
Manipulate the OpenAPI document after it is generated. You can mutate the document in any way you like, especially for those functions currently we don't support.
configureHttpRouteFilter(c: (filter: HttpRouteFilter) => void): OpenAPIModule
Configure the HttpRouteFilter
you are using, which allows you to include or exclude any paths you want. View its type to learn how to use it.
Define controllers
In most cases, deepkit-openapi
just works for your existing project. However, there are limitations of deepkit
and deepkit-openapi
that limit your way of writing codes.
You need to define your controllers using @http.controller()
and @http.GET
or @http.<METHOD>
.
@http.controller()
class UserController {
@http.GET("/user/:id").response<ReadUser>(200, "Read a User by its ID")
read(id: number) {
return db.find((user) => user.id === id);
}
}
The code above maps to the following OpenAPI:
# ...
paths:
"/user/{id}":
get:
tags:
- user
operationId: getUserRead
parameters:
- in: path
name: id
schema:
type: number
required: true
responses:
"200":
description: Read a User by its ID
content:
application/json:
schema:
$ref: "#/components/schemas/ReadUser"
components:
schemas:
ReadUser:
type: object
properties:
id:
type: number
name:
type: string
email:
type: string
required:
- id
- name
- email
Please pay attention to how each part of the controller correponds to the generated document:
- You need to specify the return type in
response
decorator. In fact, you can also specify it by explictly marking the return type. However, theresponse
has higher priority, since if both exist, the returning value will be converted to the type in the decorator. Note that this typing is by-nature unsafe because any errors could return other than your given response. - The parameters in query, body and path are automatically documented.
HttpQueries
are supported so you can reuse grouped queries. Currently, there are no ways to document header parameters. - The HTTP method name, controller name and handler method name makes up the
operationId
. - All interfaces or classes used in the schema will be kept and reused in
components.schemas
. You are responsible for keeping their names unique. The name is generated based on the type name and its concrete type arguments. - Since deepkit doesn't type the
content-type
yet, we do not support response and request types other thanapplication/json
.
Supported types
The following types are supported:
- never
- any
- unknown
- void
- object (arbitary objects)
- string
- number
- boolean
- bigint
- null
- undefined
- literal
- basic literals are mapped to single-valued enums.
- template literal
- just mapped to string
- class and interface
- array
- enum
- union
Types not supported yet:
- Special types that are mapped to basic types by
serializer
, likeDate
.
Types not planned to support:
- Generic types
- Intersection types. Use inheritance instead.
- Regex
- Functions
- Other types that make no sense once serialized.
Define your API types in TypeScript
With deepkit, you can enjoy the simplicity of manipulating your types for validation:
For example, you have a User
, now you want CreateUser
for user sign up, and ReadUser
for reading users. In a simple settings, we have following types that work well with plain deepkit.
interface User {
id: number;
name: string;
password: string;
}
type ReadUser = Omit<User, 'password'>;
type CreateUser = Omit<User, 'id'>;
The example above gives us the following OpenAPI schemas:
components:
schemas:
ReadUser:
type: object
properties:
id:
type: number
name:
type: string
required:
- id
- name
CreateUser:
type: object
properties:
name:
type: string
password:
type: string
required:
- name
- password
User:
type: object
properties:
id:
type: number
name:
type: string
password:
type: string
required:
- id
- name
- password
Contributing
Thank you for reading! If you want to contribute to this project, take a look at DEVELOPMENT.md. This is a monorepo project based on yarn@1 and deepkit's compiler. Note that deepkit and the deepkit community is still young, and any changes might break this library. Use it and devote yourself at your own risk!