@criteria/openapi
TypeScript icon, indicating that this package has built-in type declarations

0.9.3 • Public • Published

OpenAPI

TypeScript type definitions and functions for the OpenAPI specification.

Table of Contents

Overview

This package provides TypeScript type definitions and functions for working with OpenAPI documents. It is intended to be used by tooling authors, to build OpenAPI tools in TypeScript across various versions of the specification easily.

This package also contains functions to dereference OpenAPI documents into plain JavaScript objects according to the reference resolution rules of the specification.

The following versions are currently supported:

Getting Started

Install OpenAPI using npm:

npm install @criteria/openapi

Let's define a minimal OpenAPI document in code to get started:

import { OpenAPI } from '@criteria/openapi/v3.1'

const openAPI: OpenAPI = {
  openapi: '3.1.0',
  info: {},
  paths: {
    '/people': {
      get: {
        responses: {
          '200': {
            $ref: '#/components/responses/PersonList'
          }
        }
      }
    }
  },
  components: {
    responses: {
      PersonList: {
        description: 'List of person objects',
        content: {
          'application/json' {
            schema: {
              type: 'array',
              items: {
                $ref: '#/components/schemas/Person'
              }
            }
          }
        }
      }
    },
    schemas: {
      Person: {
        type: 'object',
        title: 'person',
        properties: {
          name: {
            $ref: '#/$defs/requiredString'
          },
          email: {
            $ref: '#/$defs/requiredString'
          }
        },
        $defs: {
          requiredString: {
            title: 'requiredString',
            type: 'string',
            minLength: 1
          }
        }
      }
    }
  }
}

Right now if we tried to inspect any responses of the GET /people operation we would get undefined:

console.log(openAPI.paths['/people'].get.responses['200'].content)
// undefined

We can use the dereferenceOpenAPI() function to transform our OpenAPI object with $ref values into plain JavaScript objects without any references:

import { OpenAPI, DereferencedOpenAPI, dereferenceOpenAPI } from '@criteria/openapi/v3.1'

const openAPI: OpenAPI = {
  /* same as above */
}

const dereferencedOpenAPI: DereferencedOpenAPI = dereferenceOpenAPI(openAPI)

console.log(JSON.stringify(openAPI.paths['/people'].get.responses['200'].content['application/json']))
// prints the Person schema

The return type of dereferenceOpenAPI(openAPI) is DereferencedOpenAPI instead of OpenAPI. This is a TypeScript type alias that replaces Reference objects with their dereferenced values.

JSON Schema compatibility

The OpenAPI specification makes use of the JSON Schema specification to describe Schema objects, with some modifications. Starting with v3.1, the OpenAPI specification also supports explicitly specifying which dialect of JSON Schema to use, both globally using the jsonSchemaDialect field and per schema using the $schema keyword.

This package's supports of JSON Schema is described in this table:

OpenAPI version JSON Schema draft Modifications
v3.1 Draft 2020-12 See the Schema object specification.
v3.0 Draft 04 See the Schema object specification.

Installation

npm install @criteria/openapi

Usage

Importing type definitions

Type names are suffixed with the version that they correspond to:

import { OpenAPIv3_0, OpenAPIv3_1 } from '@criteria/openapi'

const fooOpenAPI: OpenAPIv3_0 = {
  openapi: '3.0.3'
}

const barOpenAPI: OpenAPIv3_1 = {
  openapi: '3.1.0'
}

If you only want to import types for a specific version, you can import more concise types from the version-specific module:

import { OpenAPI } from '@criteria/openapi/v3.0'

const openAPI: OpenAPI = {
  openapi: '3.0.3'
}
import { OpenAPI } from '@criteria/openapi/v3.1'

const openAPI: OpenAPI = {
  openapi: '3.1.0'
}

The webhooks field is a new field introduced in version 3.1. Trying to define webhooks on a v3.0 document is caught by the TypeScript compiler:

import { OpenAPIv3_0 } from '@criteria/json-schema'

const openAPI: OpenAPIv3_0 = {
  webhooks: {}
}
// 'webhooks' does not exist in type 'OpenAPI<Reference>'

Dereferencing OpenAPI documents

Dereferencing refers to the process of transforming an OpenAPI document by replacing occurences of $ref with the actual component being referenced.

The dereferenceOpenAPI(openAPI) functions provided by this package observe the following behaviour:

  • The OpenAPI document is treated as immutable. The return value is a copy of the input data.
  • Object identity is maintained. The dereferenced OpenAPI document is the same JavaScript instance as the value that was referenced.
  • Circular references are preserved. Recursive or circular references in the input OpenAPI document will be replicated in the dereferenced output.

The following example demonstrates this behaviour:

import { dereferenceOpenAPI } from '@criteria/openapi/v3.1'

const inputOpenAPI: OpenAPI = {
  openapi: '3.1.0'
  components: {
    schemas: {
      Person: {
        type: 'object',
        title: 'person',
        properties: {
          name: {
            $ref: '#/components/schemas/RequiredString'
          },
          children: {
            type: 'array',
            items: {
              $ref: '#/components/schemas/Person'
            }
          }
        }
      },
      RequiredString: {
        title: 'requiredString',
        type: 'string',
        minLength: 1
      }
    }
  }
}

const dereferencedOpenAPI = dereferenceOpenAPI(inputOpenAPI)

console.log(dereferencedOpenAPI === inputOpenAPI)
// false, input data is not mutated

console.log(dereferencedOpenAPI.components.schemas['Person'].properties.name === dereferencedOpenAPI.components.schemas['RequiredString'])
// true, object identity is maintained

console.log(dereferencedOpenAPI.components.schemas['Person'].properties.children.items === dereferencedOpenAPI.components.schemas['Person'])
// true, circular references are supported

Retrieving external documents

It's common for API definition authors to organize complex OpenAPI definitions as multiple shared files. You can provide a custom retrieve function to dereferenceOpenAPI() in order to perform an filesystem or network operations.

The following example dereferences an OpenAPI document that references other files as relative paths from the same directory.

import { readFileSync } from 'fs'

const baseURI = resolve(__dirname, 'openapi.json')

const retrieve = (uri: string) => {
  const data = readFileSync(uri, { encoding: 'utf8' })
  return JSON.parse(data)
}

const dereferencedSchema = dereferenceJSONSchema(rootSchema, { baseURI, retrieve })

The argument passed to your retrieve function is a URI that has been resolved according to the base URI that you specified. Note that this URI is an identifier and not necessarily a network locator. In the case of a network-addressable URL, a schema need not be downloadable from its canonical URI.

Acknowledgments

Many test cases have been ported from @apidevtools/json-schema-ref-parser.

License

This project is MIT licensed.

About Criteria

Criteria is a collaborative platform for designing APIs.

Two types of tooling

  • WOrking with OpenAPI documents - serializing, bundling, importing exporting
  • Working with OpenAPI data at runtime, request/response validation, codegen etc.

Foundational types to serve both use cases OpenAPI, DereferencedOpenAPI DereferencedOpenAPI substitutes never for Reference, so you' don't need to to tahype narrowing in code hooks, onDereference() -> stash serialization context

@criteria/openapi

TypeScript type definitions and functions for the OpenAPI specification.

  • TypeScript support, including type narrowing.
  • Immutable values
  • Sync or async
  • Validation
  • DereferencedOpenAPI distinct to OpenAPI

Getting Started

npm install @criteria/openapi

Usage

import { OpenAPI } from '@criteria/openapi'

Usage

  • Validating objects conform to interface
    • Obtaining validators
  • Working with references

    • Dereferencing

Accessing TypeScript types

import { OpenAPI } from '@criteria/openapi'

const openAPI: OpenAPI = {
    ...
}

Narrow values to OpenAPI types using the isOpenAPI function.

import { OpenAPI, isOpenAPI } from '@criteria/openapi'

const value: any = // ...

if (isOpenAPI(value)) {
  // value is OpenAPI
}
import { OpenAPI, isOpenAPI, strictValidator } from '@criteria/openapi'

if (isOpenAPI(value, { validator: strictValidator })) {
  // value is OpenAPI
}

You can access specific versions of the specification.

import { OpenAPI as OpenAPIv3_0, isOpenAPI as isOpenAPIv3_0 } from '@criteria/openapi/v3.0'
import { OpenAPI as OpenAPIv3_1, isOpenAPI as isOpenAPIv3_1 } from '@criteria/openapi/v3.1'

Validating objects conform to the OpenAPI specification

import { validateOpenAPI, ValidationError } from '@criteria/openapi'

const value: any = // ...

try {
    validateOpenAPI(value)
    const validOpenAPI = value as OpenAPI
    // value is OpenAPI
} catch (error: ValidationError) {

}

Dereferencing OpenAPI objects

TypeScript type predicates

import { OpenAPI, isOpenAPI } from '@criteria/openapi'

isOpenAPI(obj, { validate: false }) // default
isOpenAPI(obj, { validate: true }) // equivalent to isOpenAPI(obj, { validate: validateOpenAPI })
isOpenAPI(obj, { validate: (obj) => { ... } })

// Example
const obj: unknown = {... }
if (isOpenAPI(obj)) {
    // obj is OpenAPI
}

Validating

import { OpenAPI, validateOpenAPI } from '@criteria/openapi'

const obj: unknown = {... }
if (validateOpenAPI(obj)) {
    // obj is OpenAPI
}

validateOpenAPI(obj, { strict: false }) // default
validateOpenAPI(obj, { strict: true, validateExtension: (field, value, object, pointer) })

Obtaining validators

const strictValidator = openAPIValidator({ strict: true })
strictValidator(obj)

Type-safe dereferenced objects

import { OpenAPI, DereferencedOpenAPI } from '@criteria/openapi'

const openAPI: OpenAPI = { ... }
const schema = openAPI.components.schema // Schema | Reference

const dereferencedOpenAPI: DereferencedOpenAPI = { ... }
const schema = dereferencedOpenAPI.components.schema // Schema

Dereferencing

import { DereferencedOpenAPI, isDereferencedOpenAPI, validateDereferencedOpenAPI } from '@criteria/openapi'
const dereferencedOpenAPI = dereferenceOpenAPI(openAPI)
const dereferencedOpenAPI = dereferenceOpenAPI(openAPI, { retrieve: (uri) => object })

Readme

Keywords

Package Sidebar

Install

npm i @criteria/openapi

Weekly Downloads

1

Version

0.9.3

License

MIT

Unpacked Size

148 kB

Total Files

179

Last publish

Collaborators

  • jamesmoschou-criteria