inversify-express-typebox-openapi
v0.1.0
Published
Generate OpenAPI specification from inversify-express-utils controllers
Downloads
9
Maintainers
Readme
Inversify Express Typebox OpenAPI
Generate OpenAPI specification during runtime from inversify-express-utils controllers and @sinclair/typebox schemas.
Features
- Generates OpenAPI v3.1 specification at runtime
- Requires no pre-compilation step
- Integrates with inversify-express-utils
- Integrates with @sinclair/typebox
- Allows for manipulation through the openapi3-ts package's builder
Usage
Installation
npm install inversify-express-typebox-openapi
Example
export enum UserState {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending',
}
export const userStateSchema = Type.Enum(UserState, { $id: 'UserState' });
export const userSchema = Type.Object(
{
id: Type.Number(),
name: Type.String(),
email: Type.String({}),
createdAt: Type.String({ format: 'date-time' }),
state: userStateSchema,
},
{ $id: 'User' },
);
export type User = Static<typeof userSchema>;
@Controller('/users')
@Tags('User')
@Security({ bearerAuth: ['read:user'] })
export class UserController {
@Get('me')
@Response(200, {
description: 'The user from the session',
content: { schema: userSchema },
})
@Response(401, { description: 'Unauthorized' })
@Security({ bearerAuth: ['read:session'] })
public getUserFromSession(
@injectResponse() res: ExpressResponse,
@Cookie('sessionId', Type.String()) sessionId: string,
): void {
// ...
}
@Get('/')
@Response(200, {
description: 'List of all users',
content: { schema: Type.Array(userSchema) },
})
@OperationId('getAllUsers')
public get(
@Query('state', Type.Optional(userStateSchema))
userState?: UserState,
@Header('Accept-Language', Type.Optional(Type.String()), {
description: 'Falls back to english if not provided',
})
_acceptLanguage?: string,
): User[] {
// ...
}
@Deprecated()
@Get('/active')
@Response(200, {
description: 'List of all active users',
content: { schema: Type.Array(userSchema) },
})
public getAllActiveUsers(): User[] {
// ...
}
@Get('/:userId')
@Response(200, {
description: 'The requested user',
content: { schema: userSchema },
})
@Response(404, { description: 'User not found' })
public getUserById(
@Path('userId', Type.Number()) userId: number,
@injectResponse() res: ExpressResponse,
@Header('Accept-Language', Type.Optional(Type.String()))
_acceptLanguage?: string,
): void {
// ...
}
@Post('/')
@Response(201, {
description: 'The created user',
content: { schema: userSchema },
})
@Security({ bearerAuth: ['write:user'] })
public createUser(
@Body(userSchema) user: User,
@injectResponse() res: ExpressResponse,
): void {
// ...
}
@Put('/:userId')
@Response(200, { description: 'The updated user' })
@Response(404, { description: 'User not found' })
@Security({ bearerAuth: ['write:user'] })
public updateUser(
@Path('userId', Type.Number()) userId: number,
@Body(userSchema) user: User,
@injectResponse() res: ExpressResponse,
): void {
// ...
}
@Patch('/:userId/state')
@Response(200, {
description: 'The updated user',
content: { schema: userSchema },
})
@Response(404, { description: 'User not found' })
@Security({ bearerAuth: ['write:user'] })
public patchUserState(
@Path('userId', Type.Number()) userId: number,
@Body(userStateSchema) userState: UserState,
@injectResponse() res: ExpressResponse,
): void {
// ...
}
@Delete('/:userId')
@Response(204, { description: 'User deleted' })
@Response(404, { description: 'User not found' })
@Security({ bearerAuth: ['delete:user'] })
public deleteUser(
@Path('userId', Type.Number()) userId: number,
@injectResponse() res: ExpressResponse,
): void {
// ...
}
}
References
Create identifiable objects
This package supports references. It will add any object that has an $id
property to the components
map of the OpenApi specification. There is a helper function for defining identifiable/referenceable objects called identifiable
. The value of $id
will be used as the key within the components map.
import { identifiable } from 'inversify-express-typebox-openapi';
import { type ExampleObject } from 'openapi3-ts/oas31';
const postExample = identifiable<ExampleObject>(
{
value: {
id: 1,
content: 'This is a post',
},
},
{ $id: 'PostExample' },
);
Create identifiable Typebox schemas
Typebox schemas support this out of the box:
import { Type } from '@sinclair/typebox';
export const postSchema = Type.Object(
{
id: Type.Number(),
content: Type.String(),
},
{ $id: 'PostSchema' },
);
withoutId
helper
In case the create identifiable objects need to be reused without their $id
property, the helper function withoutId
can be used.
import { withoutId } from 'inversify-express-typebox-openapi';
const example = withoutId({
$id: 'example'
id: 1,
});
// example = { id: 1 }
Decorators
injectRequest
Is an alias for inversify-express-utils request
decorator. May be extended with functionality at a later stage of development.
injectResponse
Is an alias for inversify-express-utils response
decorator. May be extended with functionality at a later stage of development.
injectNext
Is an alias for inversify-express-utils next
decorator. May be extended with functionality at a later stage of development.
AllowEmtpyValue
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Sets the ability to pass empty-valued parameters. This is valid only for query parameters and allows sending a parameter with an empty value.
class ExampleController {
public getUsers(
@AllowEmptyValue() @Query('status', Type.String()) status: string,
) {
// ....
}
}
This can also be achieved through the Query
decorator:
class ExampleController {
public getUsers(
@Query('status', Type.String(), { allowEmptyValue: true }) status: string,
) {
// ....
}
}
AllowReserved
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify whether the parameter value SHOULD allow reserved characters, as defined by [RFC3986] :/?#[]@!$&'()*+,;=
to be included without percent-encoding.
class ExampleController {
public getUsers(
@AllowReserved() @Query('q', Type.String()) searchQuery: string,
) {
// ....
}
}
This can also be achieved through the Query
decorator:
class ExampleController {
public getUsers(
@Query('q', Type.String(), { allowReserved: true }) searchQuery: string,
) {
// ....
}
}
Body
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a body parameter.
const userSchema = Type.Object({
id: Type.Number(),
name: Type.String(),
});
type User = Static<typeof userSchema>;
// ...
class ExampleController {
// ...
public createUser(@Body(userSchema) userDto: User) {
// ....
}
}
Cookie
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a cookie parameter.
// ...
class ExampleController {
// ...
public get(@Cookie('Cookie', Type.String()) cookie: string) {
// ....
}
}
Delete
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a delete method.
// ...
class ExampleController {
@Delete('/users/{userId}')
public deleteUser(/* ... */) {
// ....
}
}
Deprecated
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ✅ |
Flag a parameter, a method or all methods of a controller as deprecated. Deprecated parameters will have required
set to false
.
// ...
@Deprecated()
class ExampleController {
// ...
}
// ...
class ExampleController {
// ...
@Deprecated()
public getUser() {
// ...
}
}
// ...
class ExampleController {
// ...
public getUser(@Deprecated() @Path('userId', Type.String())) {
}
}
Description
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ✅ |
Specify a description for methods and parameters.
class ExampleController {
@Description('Get all users')
public getUsers() {
// ....
}
}
When used on a controller, it is applied to each of its methods.
Parameter descriptions can also be achieved through any of the parameter decorators:
class ExampleController {
public getUsers(
@Query('q', Type.String(), { description: 'Search query' })
searchQuery: string,
) {
// ....
}
}
Example
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify an example for a parameter value.
class ExampleController {
public getUsers(
@Example('Search query') @Query('q', Type.String()) searchQuery: string,
) {
// ....
}
}
This can also be achieved through any of the parameter decorators:
class ExampleController {
public getUsers(
@Query('q', Type.String(), { example: 'Search query' }) searchQuery: string,
) {
// ....
}
}
Examples
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a map of examples for a parameter value:
class ExampleController {
public createUser(
@Examples({
admin: {
value: { name: 'admin', role: 'admin' },
},
guest: {
value: { name: 'guest', role: null },
},
})
@Body(userSchema)
userDto: User,
) {
// ....
}
}
This can also be achieved through any of the parameter decorators:
class ExampleController {
public createUser(
@Body(userSchema, {
examples: {
admin: {
value: { name: 'admin', role: 'admin' },
},
guest: {
value: { name: 'guest', role: null },
},
},
})
userDto: User,
) {
// ....
}
}
Explode
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify that property values of the type array
or object
generate separate parameters for each value of the array, or key-value-pair of the map.
// ...
class ExampleController {
// ...
public getUsers(
@Explode() @Query('id', Type.Array(Type.String()) ids: string[],
) {
// ....
}
}
This can also be achieved through any of the parameter decorators:
// ...
class ExampleController {
// ...
public getUsers(
@Query('id', Type.Array(Type.String(), { explode: true }) ids: string[],
) {
// ....
}
}
ExternalDocs
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ✅ |
Specify external documentation.
// ...
@ExternalDocs({
description: 'Official documentation',
url: 'https://example.org',
})
class ExampleController {
// ...
public getUsers() {
// ....
}
}
// ...
class ExampleController {
// ...
@ExternalDocs({
description: 'Official documentation',
url: 'https://example.org',
})
public getUsers() {
// ....
}
}
// ...
class ExampleController {
// ...
public getUser(
@ExternalDocs({
description: 'Official documentation',
url: 'https://example.org',
})
@Path('id', Type.String()))
id: string
{
// ....
}
}
Get
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a get method.
// ...
class ExampleController {
@Get('/users')
public getUsers() {
// ....
}
}
Head
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a head method.
// ...
class ExampleController {
@Head('/file/xyz.zip')
public download() {
// ....
}
}
Header
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a header parameter.
// ...
class ExampleController {
// ...
public get(@Header('Authorization', Type.String()) authToken: string) {
// ....
}
}
OperationId
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ❌ |
Specify a custom operation id for a method. By default, the operation id is the method name.
class ExampleController {
@OperationId('customOperationId')
@Get('/users')
public getUsers() {
// ....
}
}
This example will result in the operation id ExampleController_customOperationId
.
When used on a controller, the prefix (by default the controller name) is replaced with the custom operation id. The following example will result in the operation id customControllerName_customOperationId
:
@OperationId('customControllerName')
class ExampleController {
@OperationId('customOperationId')
@Get('/users')
public getUsers() {
// ....
}
}
Patch
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a patch method.
// ...
class ExampleController {
@Patch('/users/{userId}')
public updateUser() {
// ....
}
}
Path
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a path parameter.
// ...
class ExampleController {
@Get('/users/{userId}')
public getUser(@Path('userId', Type.String()) userId: string) {
// ...
}
}
Post
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a post method.
// ...
class ExampleController {
@Post('/users')
public createUser() {
// ....
}
}
Put
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a put method.
// ...
class ExampleController {
@Put('/users/{userId}')
public replaceUser() {
// ....
}
}
Query
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Specify a query parameter.
// ...
class ExampleController {
// ...
public getUsers(@Query('query', Type.String()) query: string) {
// ...
}
}
Response
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a possible response for a method.
class ExampleController {
// ...
@Response(200, {
description: 'IDs of users',
content: { schema: Type.Array(Type.Number()) },
})
public getUserIds() {
// ...
}
}
Use default
instead of an HTTP status code for a default error response.
Security
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ❌ |
Specify the security requirements for a method.
// ...
@Security({ securityScheme: ['scopeA', 'scopeB'] })
class ExampleController {
// ...
}
When used on a controller, it is applied to each of its methods.
Multiple @Security
decorators onto one controller/method extend the requirements.
Style
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ❌ | ✅ |
Describes how the parameter value will be serialized depending on the type of the parameter value.
// ...
class ExampleController {
// ...
public getUser(
@Style('simple') @Path('userId', Type.String()) status: string,
) {
// ....
}
}
This can also be achieved through any of the parameter decorators:
// ...
class ExampleController {
// ...
public getUser(
@Path('userId', Type.String(), { style: 'simple' }) status: string,
) {
// ....
}
}
Summary
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ❌ | ✅ | ❌ |
Specify a summary for an operation.
// ...
class ExampleController {
// ....
@Summary('Retrieves all the users form the database')
public getUser() {
// ....
}
}
Tags
| Controller | Method | Parameter | | :--------: | :----: | :-------: | | ✅ | ✅ | ❌ |
Specify tags for a method.
// ...
class ExampleController {
// ....
@Tags('V1', 'User')
public getUser() {
// ....
}
}
When used on a controller, it is applied to each of its methods.