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 🙏

© 2025 – Pkg Stats / Ryan Hefner

typera-openapi

v2.3.1

Published

Generate OpenAPI spec from typera routes

Downloads

489

Readme

typera-openapi - OpenAPI generator for typera

Build

typera-openapi is an tool that automatically creates OpenAPI v3 definitions for projects that use typera for routes.

Upgrading to v2? See the upgrading instructions.

Table of Contents

Getting started

Install typera-openapi:

npm install typera-openapi

Your route files must have a single default export that exports a typera router. JSDoc comments serve as additional documentation:

// src/my-routes.ts

import { Response, Route, route, router } from 'typera-express'

interface MyResult {
  /** The JSDoc text is used as a description for object properties */
  field: number
}

const bodyCodec = t.type({
  /** Descriptions are also supported in io-ts codecs
   * @example Lets you set an example value for a particular property
   */
  name: t.string
})

/**
 * Routes can also have a description. Note that descriptions are entirely optional.
 *
 * @summary You can also set a short summary
 * @tags Tag1, Tag2
 *
 * @routeParam myRouteParam Description for route parameter
 *
 * @response 200 Success response description.
 * @response 400 Another description for a response. This one
 * spans multile lines.
 */
const myRoute: Route<Response.Ok<MyResult> | Response.BadRequest<string>> =
  route.post(...).use(Parser.body(bodyCodec)).handler(...)

// ...

/**
 * @prefix /api
 * @tags Tag3
 */
export default router(myRoute, ...)

// The optional @prefix JSDoc tag prepends the prefix to all route paths.
// Tags from router are added to all its routes.

Run the typera-openapi tool giving paths to your route files as command line arguments:

npx typera-openapi src/my-routes.ts

This creates src/my-routes.openapi.ts which contains the OpenAPI definitions.

Use the definitions in your app to serve documentation:

// src/app.ts

import * as express from 'express'
import { OpenAPIV3 } from 'openapi-types'
import * as swaggerUi from 'swagger-ui-express'
import { prefix } from 'typera-openapi'

import myRoutes from './my-routes'
import openapi from './openapi'

const openapiDoc: OpenAPIV3.Document = {
  openapi: '3.0.0',
  info: {
    title: 'My cool API',
    version: '0.1.0',
  },
  ...openapi,
}

const app = express()
app.use('/api', myRoutes.handler())
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapiDoc))
app.listen(3000)

CLI

typera-openapi [options] FILE...

Generate OpenAPI definitions for routes found in the given files.

Options:

-o OUTFILE, --outfile OUTFILE

Output file name. Must end in either .ts or .json.

--prettify, -p

Apply prettier formatting to output files.

--check, -c

Check that the output file is up-to-date without actually writing it. If the file is outdated, print an error and exit with status 1. Useful for CI.

How it works?

typera-openapi uses TypeScript type information to generate OpenAPI definitions. Methods, paths, route params, query params, request and response bodies, and request and response headers are determined by looking up the type information provided by the TypeScript compiler API.

For example, assume we have this route:

/**
 * @response 200 User created successfully
 * @response 400 Validation error
 */
const createUser: Route<Response.Ok<User> | Response.BadRequest<string>> = route
  .post('/users')
  .use(Parser.body(createUserBody))
  .handler(async (request) => { ... })
  • Response descriptions are looked up from the JSDoc comment. Other tags are also available.
  • Response body and header types are available in the type of the createUser variable. The explicit type annotation would not even be needed, because the compiler can infer the type.
  • Method and path are available in the .post('/users') call.
  • The rest are looked up by inspecting the type of the request parameter of the route handler function.

This has one caveat: The request type must match the input you expect your user to send. Assume the createUserBody codec is like this:

const createUserBody = t.type({
  name: t.string,
  shoeSize: t.number,
  allowMarketing: BooleanFromString, // from io-ts-types
})

The name and shoeSize fields are fine, since both t.string and t.number map their input type to the same output type. But the allowMarketing field is problematic, since BooleanFromString takes a string as an input, but converts it to boolean.

So the JSON input this route expects is:

type Input = {
  name: string
  shoeSize: number
  allowMarketing: string
}

But the type of request.body that the compiler sees is:

type Body = {
  name: string
  shoeSize: number
  allowMarketing: boolean
}

For this reason, you shouldn't use decoders that change the type of the input directly. If needed, you should instead add another step for converting the data from input to the format you expect. This can be achieved with a custom middleware, for example.

The Date type

JSON doesn't have a native Date type. As explained above, when typera-openapi encounters a Date type, it doesn't know from which input type it is parsed, so it uses string and sets format to date-time, because this is how JSON.stringify encodes Date objects by default. In the future we may add a way to override this behavior.

Reference

Terms:

  • Route means an OpenAPI operation, i.e. an endpoint you can request.
  • route is the typera object that lets you create routes, see the docs.
  • Route handler is the function passed to .handler() when defining a route
  • request is the sole parameter of the route handler function.
  • Route type is the type of the route variable returned by route.get() etc.
  • Router is the value returned by router().

For each route, typera-openapi determines the following information:

| Information | Source | | ------------ | --------------------------------------------------------------------------------------- | | method | Which route method is called, e.g. route.get() | | path | The parameter of e.g. route.get() | | summary | JSDoc comment's @summary tag | | description | JSDoc comment's text | | tags | JSDoc comment's @tags | | operationId | Name of the variable that holds the route, override with JSDoc comment's @operationId | | parameters | See table below | | request body | See table below | | responses | See table below |

The JSDoc comment of the router can be used to add information to all its routes:

| Information | Source | | ----------- | ----------------------------------------------------- | | path prefix | JSDoc comment's @path tag is prefixed to all routes | | tags | JSDoc comment's @tags are added to all routes |

OpenAPI parameters covers all the other input expect the request body:

| Parameter | Source | | --------- | ---------------------------------------------------------------------------------------------- | | path | Route parameter captures | | query | The type of request.query (the output of Parser.query or custom middleware) | | header | The type of request.headers (the output of Parser.headers or custom middleware) | | cookie | The type of request.cookies (the output of Parser.cookies or custom middleware) |

OpenAPI allows different request body types per content type, but typera-openapi only allows one.

| Body field | Source | | ------------ | ----------------------------------------------------------------------------- | | content type | request.contentType, or 'application/json' if not defined | | schema | The type of request.body (the output of Parser.body or custom middleware) |

A route can have multiple responses. These are modeled in the route type as Route<Response1 | Response2 | ...>. See the docs. Each response type adds a response to the OpenAPI document.

| Response field | Source | | -------------- | ---------------------------------------------- | | status | Response's Status type (number literal type) | | description | JSDoc comment's @response tag | | content type | See below | | content schema | Response's Body type | | headers | Response's Headers type |

Response's content type is determined as follows:

  • text/plain if the body type is string or number
  • application/octet-stream for streaming responses
  • application/json otherwise

Releasing

$ yarn version --new-version <major|minor|patch>
$ yarn publish
$ git push origin main --tags

Open https://github.com/akheron/typera-openapi/releases, edit the draft release, select the newest version tag, adjust the description as needed.