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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@martin.xyz/openapi-decorators

v0.1.2

Published

Auto-Generate OpenAPI specifications from Typescript decorators

Downloads

997

Readme

@martin.xyz/openapi-decorators

Generate OpenAPI compliant specifications using Typescript Decorators

npm version npm downloads bundle size install size license

@martin.xyz/openapi-decorators is a framework agnostic library to automatically generate OpenAPI schemas and documentation by using Typescript decorators and metadata.

class User {
  @ApiProperty()
  declare id: number;

  @ApiProperty()
  declare name: string;

  @ApiProperty({ required: false })
  declare mobile?: string;
}

class UsersController {
  @ApiOperation({
    methods: ["get"],
    path: "/users",
    summary: "List users"
  })
  @ApiResponse({ type: [User] })
  async list() {
    ...
  }
}

await generateDocument({
  controllers: [UsersController],
  document: {
    info: {
      title: "My Api",
      version: "1.0.0",
    },
  },
});

Getting Started

Setup

Install @martin.xyz/openapi-decorators and reflect-metadata using your favorite package manager.

npm install @martin.xyz/openapi-decorators

Import reflect-metadata in your main file.

import 'reflect-metadata'

// Rest of your app

Enable emitDecoratorMetadata and experimentalDecorators in your tsconfig.json.

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

Create your OpenAPI document

To get started, you can use the generateDocument function to create an (almost) empty documentation. You can define a base document that will be merged with the generated one.

import 'reflect-metadata'
import { generateDocument } from '@martin.xyz/openapi-decorators'

const document = await generateDocument({
  controllers: [],
  document: {
    info: {
      title: 'My API',
      version: '1.0.0',
    },
  },
})

console.log(document) // <- Your generated OpenAPI specifications

Create your first Controller

A controller is a simple class where each methods could be an Operation. In the following example we have a UsersController which declares an operation GET /users that returns a list of Users.

import { ApiOperation, ApiResponse } from '@martin.xyz/openapi-decorators/decorators'
import User from '../schemas/user'

export default class UsersController {
  @ApiOperation({
    methods: ['get'],
    path: '/users',
    summary: 'List users',
  })
  @ApiResponse({ type: [User] })
  async list() {
    // ...your logic
  }
}

Create your first schema

In our controller we define the response of your operation to be [User] (a list of users). We now need to create this model.

By using the @ApiProperty decorator on class we can define the properties of our schema.

Unlike other libraries like @nestjs/swagger, every element of your OpenAPI schema is lazy-loaded. Your models will only be part of your documentation if it is used.

Register your controller

Now that we have our controller ready, we can include it when generating our document.

import 'reflect-metadata'
import { generateDocument } from '@martin.xyz/openapi-decorators'
import UsersController from './controllers/users_controller'

const document = await generateDocument({
  controllers: [UsersController],
  document: {
    info: {
      name: 'My API',
      version: '1.0.0',
    },
  },
})

console.log(document) // <- Your generated OpenAPI specifications

Decorators

Decorators are used to enrich your OpenAPI specifications. They can be applied on a Controller, a Method or a Model. They are all prefixed with Api.

You can find the list of available operators in the source code.

UI Integrations

This library brings some utilities to easily display your API documentation using different providers.

Scalar

import { generateScalarUI } from '@martin.xyz/openapi-decorators/ui'

const html = generateScalarUI('http://localhost:3000/api')

Swagger UI

import { generateSwaggerUI } from '@martin.xyz/openapi-decorators/ui'

const html = generateSwaggerUI('http://localhost:3000/api')

Rapidoc

import { generateRapidocUI } from '@martin.xyz/openapi-decorators/ui'

const html = generateRapidocUI('http://localhost:3000/api')

Type Loader

A Type Loader transforms a JavaScript Object into a SchemaObject. It gives the ability to extends the inference capabilities when generating a document.

For example String will be transformed into { type: 'string' }.

Custom Type Loader

In this example we will see how to create a custom loader for the Luxon DateTime.

With the following schema we have a property that cannot be loaded as is is unknown by the Document generator.

import { DateTime } from 'luxon'

export default class User {
  @ApiProperty()
  createdAt: DateTime
}

We assume that our API will serialize our DateTime into a string containing the date in ISO format. The user could explicitly define the type @ApiProperty({ type: 'string' }) but as this library goal is to provide a great DX it is possible to create a custom type loader.

import { generateDocument } from '@martin.xyz/openapi-decorators'
import { TypeLoaderFn } from '@martin.xyz/openapi-decorators/types'
import { DateTime } from 'luxon'

const LuxonDateTimeLoader: TypeLoaderFn = (_context, value) => {
  if (value === DateTime) {
    return { type: 'string' }
  }
}

await generateDocument({
  loaders: [LuxonDateTimeLoader],
})

If you have more complex schemas to generate, you can store it in the components and return a reference instead:

const CustomLoader: TypeLoaderFn = (context, value) => {
  if (isCustom(value)) {
    const [name, schema] = generateCustomSchema(value)

    context.schemas[name] = schema

    return { $ref: `#/components/schemas/${name}` }
  }
}

Integrations

Metadata

Decorators does not contain business logic, their purpose is to store metadata to be used when generating the document. This makes it easy to integrate this library into a framework and create custom decorators.

Here is an example of a custom decorator that define an operation summary:

import { OperationMetadataStorage } from '@martin.xyz/openapi-decorators/metadata'

export function ApiSummary(summary: string): MethodDecorator {
  return (target, propertyKey) => {
    OperationMetadataStorage.mergeMetadata(target, { summary }, propertyKey)
  }
}

MetadataStorage

A MetadataStorage is a utility for managing metadata:

defineMetadata

This method sets the metadata. It overwrites existing metadata.

import { OperationMetadataStorage } from '@martin.xyz/openapi-decorators/metadata'

// Without propertyKey
OperationMetadataStorage.defineMetadata(target, { summary: 'Hello world' })

// With propertyKey
OperationMetadataStorage.defineMetadata(target, { summary: 'Hello world' }, propertyKey)

mergeMetadata

Similar to defineMetadata, this method sets the metadata but deepmerge its content with exising metadata.

import { OperationMetadataStorage } from '@martin.xyz/openapi-decorators/metadata'

// Without propertyKey
OperationMetadataStorage.mergeMetadata(target, { summary: 'Hello world' })

// With propertyKey
OperationMetadataStorage.mergeMetadata(target, { summary: 'Hello world' }, propertyKey)

getMetadata

This method retrieve the stored metadata. When used with a propertyKey you can also define withParent to deepmerge the metadata with the one defined on the class.

Custom MetadataStorage

You can create a custom metadata storage by using the createMetadataStorage function.

import { createMetadataStorage } from '@martin.xyz/openapi-decorators/metadata'

type CustomMetadata = { foo: 'bar' }

const CustomMetadataKey = Symbol('Custom')

const CustomMetadataStorage = createMetadataStorage<CustomMetadata>(CustomMetadataKey)

License

MIT licensed.