Package Exports
- serverless-openapi-documenter
- serverless-openapi-documenter/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (serverless-openapi-documenter) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
OpenAPI Generator for serverless
This will generate an OpenAPI V3 (up to v3.0.3) file for you from your serverless file. It can optionally generate a Postman Collection V2 from the OpenAPI file for you too.
Originally based off of: https://github.com/temando/serverless-openapi-documentation
Install
This plugin works for Serverless 2.x and up and only supports node.js 14 and up.
To add this plugin to your package.json:
Using npm:
npm install --save-dev serverless-openapi-documenterNext you need to add the plugin to the plugins section of your serverless.yml file.
plugins:
- serverless-openapi-documenterNote: Add this plugin after
serverless-offlineto prevent issues withString.replaceAllbeing overridden incorrectly.
Adding documentation to serverless
To Run: serverless openapi generate -o openapi.json -f json -a 3.0.3 -p postman.json
Options:
--output -o What filename the OpenAPI documentation should output under. Default: openapi.json
--format -f Whether to output the OpenAPI documentation as json or yaml. Default: json
--indent -i File indentation in spaces. Default: 2
--openApiVersion -a OpenAPI version to generate for. Default: 3.0.0
--postmanCollection -p Will generate a postman collection (from the generated openAPI documentation), in json only, if passed in. Default postman.jsonOpenAPI Mapping
| OpenAPI field | Serverless field |
|---|---|
| info.title | custom.documentation.title OR service |
| info.description | custom.documentation.description OR blank string |
| info.version | custom.documentation.version OR random v4 uuid if not provided |
| info.termsOfService | custom.documentation.termsOfService |
| info.contact | custom.documentation.contact |
| info.contact.name | custom.documentation.contact.name OR blank string |
| info.contact.url | custom.documentation.contact.url if provided |
| info.license | custom.documentation.license |
| info.license.name | custom.documentation.license.name OR blank string |
| info.license.url | custom.documentation.license.url if provided |
| externalDocs.description | custom.documentation.externalDocumentation.description |
| externalDocs.url | custom.documentation.externalDocumentation.url |
| servers[].description | custom.documentation.servers.description |
| servers[].url | custom.documentation.servers.url |
| servers[].variables | custom.documentation.servers.variables |
| tags[].name | custom.documentation.tags.name |
| tags[].description | custom.documentation.tags.description |
| tags[].externalDocs.url | custom.documentation.tags.externalDocumentation.url |
| tags[].externalDocs.description | custom.documentation.tags.externalDocumentation.description |
| path[path] | functions.functions.events.[http OR httpApi].path |
| path[path].summary | functions.functions.summary |
| path[path].description | functions.functions.description |
| path[path].servers[].description | functions.functions.servers.description |
| path[path].servers[].url | functions.functions.servers.url |
| path[path].[operation] | functions.functions.[http OR httpApi].method |
| path[path].[operation].summary | functions.functions.[http OR httpApi].documentation.summary |
| path[path].[operation].description | functions.functions.[http OR httpApi].documentation.description |
| path[path].[operation].operationId | functions.functions.[http OR httpApi].documentation.operationId OR functionName |
| path[path].[operation].deprecated | functions.functions.[http OR httpApi].documentation.deprecated |
| path[path].[operation].externalDocs.description | functions.functions.[http OR httpApi].documentation.externalDocumentation.description |
| path[path].[operation].externalDocs.url | functions.functions.[http OR httpApi].documentation.externalDocumentation.url |
| path[path].[operation].servers[].description | functions.functions.[http OR httpApi].documentation.servers.description |
| path[path].[operation].servers[].url | functions.functions.[http OR httpApi].documentation.servers.url |
| path[path].[operation].deprecated | functions.functions.[http OR httpApi].documentation.deprecated |
| path[path].[operation].parameters | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params |
| path[path].[operation].parameters.name | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.name |
| path[path].[operation].parameters.in | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params |
| path[path].[operation].parameters.description | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.description |
| path[path].[operation].parameters.required | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.required |
| path[path].[operation].parameters.deprecated | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.deprecated |
| path[path].[operation].parameters.allowEmptyValue | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.allowEmptyValue |
| path[path].[operation].parameters.style | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.style |
| path[path].[operation].parameters.explode | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.explode |
| path[path].[operation].parameters.allowReserved | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.allowReserved |
| path[path].[operation].parameters.schema | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.schema |
| path[path].[operation].parameters.example | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.example |
| path[path].[operation].parameters.examples | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.examples |
| path[path].[operation].requestBody | functions.functions.[http OR httpApi].documentation.requestBody |
| path[path].[operation].requestBody.description | functions.functions.[http OR httpApi].documentation.requestBody.description |
| path[path].[operation].requestBody.required | functions.functions.[http OR httpApi].documentation.requestBody.required |
| path[path].[operation].requestBody.content | functions.functions.[http OR httpApi].documentation.requestModels[contentType].name Links to custom.documentation.models.name |
| path[path].[operation].responses | functions.functions.[http OR httpApi].documentation.methodResponses |
| path[path].[operation].requestBody.[statusCode] | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode] |
| path[path].[operation].requestBody.[statusCode].description | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode].responseBody.description |
| path[path].[operation].requestBody.[statusCode].content | functions.functions.[http OR httpApi].documentation.methodResponses[statusCode].responseModels[contentType] Links to custom.documentation.models.name |
Configuration
To configure this plugin to generate valid OpenAPI documentation there are two places you'll need to modify in your serverless.yml file, the custom variables section and the http event section for each given function in your service.
The custom section of your serverless.yml can be configured as below:
custom:
documentation:
version: '1'
title: 'My API'
description: 'This is my API'
termsOfService: https://google.com
externalDocumentation:
url: https://google.com
description: A link to google
servers:
url: https://example.com:{port}/
description: The server
variables:
port:
enum:
- 4000
- 3000
default: 3000
description: The port the server operates on
tags:
- name: tag1
description: this is a tag
externalDocumentation:
url: https://npmjs.com
description: A link to npm
models: {}Mostly everything here is optional. A version from a UUID will be generated for you if you don't specify one, title will be the name of your service if you don't specify one.
termsOfService
Must be in the format of a url if included.
Contact
You can provide an optional contact object such as:
custom:
documentation:
contact:
name: John
url: https://example.com
email: John@example.comThese fields are optional, though url and email need to be in the format of an email address (ed: what that might be, i'm not 100% sure... go read the email RFC(s)) and a url.
License
You can provide an optional license object such as:
custom:
documentation:
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.htmlName is required but url is optional and must be in the format of a url.
Extended Fields
You can also add extended fields to the documentation object:
custom:
documentation:
x-other-field: This is an extended fieldThese fields must have x- before them, otherwise they will be ignored:
custom:
documentation:
other-field: This is an extended fieldother-field here will not make it to the generated OpenAPI schema.
These configurations can be quite verbose; you can separate it out into it's own file, such as serverless.doc.yml as below:
custom:
documentation: ${file(serverless.doc.yml):documentation}
functions:
myFunc:
events:
- http:
path: getStuff
method: get
documentation: ${file(serverless.doc.yml):endpoints.myFunc}For more info on serverless.yml syntax, see their docs.
Models
There are two ways to write the Models. Models contain additional information that you can use to define schemas for endpoints. You must define the content type for each schema that you provide in the models.
The first way of writing the model is: required directives for the models section are as follow:
name: the name of the schemadescription: a description of the schemacontentType: the content type of the described request/response (ie.application/jsonorapplication/xml).schema: The JSON Schema (website) that describes the model. You can either use inlineYAMLto define these or use either an external file schema that serverless will resolve (as below), or a reference to an externally hosted schema that will be attempted to be resolved.
custom:
documentation:
models:
- name: "ErrorResponse"
description: "This is an error"
contentType: "application/json"
schema: ${file(models/ErrorResponse.json)}
- name: "PutDocumentResponse"
description: "PUT Document response model (external reference example)"
contentType: "application/json"
schema: ${file(models/PutDocumentResponse.json)}
- name: "PutDocumentRequest"
description: "PUT Document request model (inline example)"
contentType: "application/json"
schema:
$schema: "http://json-schema.org/draft-04/schema#"
properties:
SomeObject:
type: "object"
properties:
SomeAttribute:
type: "string"The Second way of writing the models:
name: the name of the schemadescription: a description of the schemacontent: an Object made up of the contentType and the schema, as shown below
custom:
documentation:
models:
- name: "ErrorResponse"
description: "This is an error"
content:
application/json:
schema: ${file(models/ErrorResponse.json)}
- name: "PutDocumentResponse"
description: "PUT Document response model (external reference example)"
content:
application/json:
schema: ${file(models/PutDocumentResponse.json)}
- name: "PutDocumentRequest"
description: "PUT Document request model (inline example)"
content:
application/json:
schema:
$schema: "http://json-schema.org/draft-04/schema#"
properties:
SomeObject:
type: "object"
properties:
SomeAttribute:
type: "string"Model re-use
Through the magic of YAML, you can re-use models:
custom:
documentation:
...
models:
- name: "ErrorResponse"
description: "This is an error"
content:
application/json:
schema: &ErrorItem
type: object
properties:
message:
type: string
code:
type: integer
- name: "PutDocumentResponse"
description: "PUT Document response model (external reference example)"
content:
application/json:
schema:
type: array
items: *ErrorItem&ErrorItem in the above example creates a node anchor (&ErrorItem) to the ErrorResponse schema which then can be used in the PutDocumentResponse schema via the reference (*ErrorItem). The node anchor needs to be declared first before it can be used elsewhere via the reference, swapping the above example around would result in an error.
Functions
To define the documentation for a given function event, you need to create a documentation attribute for your http event in your serverless.yml file.
The documentation section of the event configuration can contain the following attributes:
summary: a short description of the methoddescription: a detailed description of the methodtags: an array of tags for this eventdeprecated: boolean indicator that indicates clients should migrate away from this functionrequestBody: contains description of the requestdescription: a description of the request body
requestModels: a list of models to describe the request bodies (see requestModels below)queryParams: a list of query parameters (see queryParams below)pathParams: a list of path parameters (see pathParams below)cookieParams: a list of cookie parameters (see cookieParams below)headerParams: a list of headers (see headerParams below)methodResponses: an array of response models and applicable status codesstatusCode: applicable http status code (ie. 200/404/500 etc.)responseBody: contains description of the responsedescription: a description of the body response
responseHeaders: a list of response headers (see responseHeaders below)responseModels: a list of models to describe the request bodies (see responseModels below) for eachContent-Type
functions:
createUser:
handler: handler.create
events:
- http:
path: create
method: post
summary:
documentation:
summary: "Create User"
description: "Creates a user and then sends a generated password email"
tags:
- tag1
externalDocumentation:
url: https://bing.com
description: A link to bing
requestBody:
description: "A user information object"
requestModels:
application/json: "PutDocumentRequest"
pathParams:
- name: "username"
description: "The username for a user to create"
schema:
type: "string"
pattern: "^[-a-z0-9_]+$"
queryParams:
- name: "membershipType"
description: "The user's Membership Type"
schema:
type: "string"
enum:
- "premium"
- "standard"
cookieParams:
- name: "SessionId"
description: "A Session ID variable"
schema:
type: "string"
headerParams:
name: "Content-Type"
description: "The content type"
schema:
type: "string"
methodResponses:
- statusCode: 201
responseBody:
description: "A user object along with generated API Keys"
responseModels:
application/json: "PutDocumentResponse"
responseHeaders:
X-Rate-Limit-Limit:
description: The number of allowed requests in the current period
schema:
type: integer
- statusCode: 500
responseBody:
description: "An error message when creating a new user"
responseModels:
application/json: "ErrorResponse"queryParams
Query parameters can be described as follow:
name: the name of the query variabledescription: a description of the query variablerequired: whether the query parameter is mandatory (boolean)schema: JSON schema (inline, file or externally hosted)
queryParams:
- name: "filter"
description: "The filter parameter"
required: true
schema:
type: "string"pathParams
Path parameters can be described as follow:
name: the name of the path parameterdescription: a description of the path parameterschema: JSON schema (inline, file or externally hosted)
pathParams:
- name: "usernameId"
description: "The usernameId parameter"
schema:
type: "string"cookieParams
Cookie parameters can be described as follow:
name: the name of the cookie parameterdescription: a description of the cookie parameterrequired: whether the cookie parameter is mandatory (boolean)schema: JSON schema (inline, file or externally hosted)
cookieParams:
- name: "sessionId"
description: "The sessionId parameter"
required: true
schema:
type: "string"headerParams - Request Headers
Request Headers can be described as follow:
name: the name of the headerdescription: a description of the headerrequired: whether the header is mandatory (boolean)schema: JSON schema (inline, file or externally hosted)
headerParams:
- name: "Content-Type"
description: "The content type"
required: true
schema:
type: "string"requestModels
The requestModels property allows you to define models for the HTTP Request of the function event. You can define a different model for each different Content-Type. You can define a reference to the relevant request model named in the models section of your configuration (see Defining Models section).
requestModels:
application/json: "CreateRequest"
application/xml: "CreateRequestXML"methodResponses
You can define the response schemas by defining properties for your function event.
For an example of a methodResponses configuration for an event see below:
methodResponse:
- statusCode: 200
responseBody:
description: Success
responseModels:
application/json: "CreateResponse"
application/xml: "CreateResponseXML"
responseHeaders:
X-Rate-Limit-Limit:
description: The number of allowed requests in the current period
schema:
type: integer
X-Rate-Limit-Remaining:
description: The number of remaining requests in the current period
schema:
type: integer
X-Rate-Limit-Reset:
description: The number of seconds left in the current period
schema:
type: integerresponseModels
The responseModels property allows you to define models for the HTTP Response of the function event. You can define a different model for each different Content-Type. You can define a reference to the relevant response model named in the models section of your configuration (see Defining Models section).
responseModels:
application/json: "CreateResponse"
application/xml: "CreateResponseXML"responseHeaders
The responseHeaders property allows you to define the headers expected in a HTTP Response of the function event. This should only contain a description and a schema, which must be a JSON schema (inline, file or externally hosted).
responseHeaders:
X-Rate-Limit-Limit:
description: The number of allowed requests in the current period
schema:
type: integerExample configuration
Please view the example serverless.yml.
Notes on schemas
Schemas can be either: inline, in file or externally hosted. If they're inline or in file, the plugin will attempt to normalise the schema to OpenAPI 3.0.X specification.
If they exist as an external reference, for instance:
schema: https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/bettercodehub.jsonWe use the plugin JSON Schema $Ref Parser to attempt to parse and resolve the references. There are limitations to this. Consider the schema:
{
"$schema": "https://json-schema.org/draft-04/schema",
"title": "Reusable Definitions",
"type": "object",
"id": "https://raw.githubusercontent.com/json-editor/json-editor/master/tests/fixtures/definitions.json",
"definitions": {
"address": {
"title": "Address",
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address"]
},
"link" : {"$refs": "./properties.json#/properties/title"}
},
"properties": {
"address" : {"$refs": "#/definitions/address"}
}
}Where the definition "link" refers to a schema held in a directory that the resolver does not know about, we will not be able to fully resolve the schema which will likely cause errors in validation of the openAPI 3.0.X specification.
Because of the dependency we use to parse externally linked schemas, we can supply our own options to resolve schemas that are more difficult than a straight forward example.
You can create your own options file: https://apitools.dev/json-schema-ref-parser/docs/options.html to pass into the dependency that contains it's own resolver to allow you to resolve references that might be in hard to reach places. In your main project folder, you should have a folder called options with a file called ref-parser.js that looks like:
'use strict'
// options from: https://apitools.dev/json-schema-ref-parser/docs/options.html
module.exports = {
continueOnError: true, // Don't throw on the first error
parse: {
json: false, // Disable the JSON parser
yaml: {
allowEmpty: false // Don't allow empty YAML files
},
text: {
canParse: [".txt", ".html"], // Parse .txt and .html files as plain text (strings)
encoding: 'utf16' // Use UTF-16 encoding
}
},
resolve: {
file: false, // Don't resolve local file references
http: {
timeout: 2000, // 2 second timeout
withCredentials: true, // Include auth credentials when resolving HTTP references
}
},
dereference: {
circular: false, // Don't allow circular $refs
excludedPathMatcher: (path) => // Skip dereferencing content under any 'example' key
path.includes("/example/")
}
}If you don't supply this file, it will use the default options.
License
MIT