npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

inversify-express-typebox-openapi

v0.1.0

Published

Generate OpenAPI specification from inversify-express-utils controllers

Downloads

9

Readme

Inversify Express Typebox OpenAPI

Generate OpenAPI specification during runtime from inversify-express-utils controllers and @sinclair/typebox schemas.

Features

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.