A library containing Swift types that encode to- and decode from OpenAPI Documents and their components.
- Usage
- Notes
- Project Status
- OpenAPI Object (
OpenAPI.Document
) - Info Object (
OpenAPI.Document.Info
) - Contact Object (
OpenAPI.Document.Info.Contact
) - License Object (
OpenAPI.Document.Info.License
) - Server Object (
OpenAPI.Server
) - Server Variable Object (
OpenAPI.Server.Variable
) - Components Object (
OpenAPI.Components
) - Paths Object (
OpenAPI.PathItem.Map
) - Path Item Object (
OpenAPI.PathItem
) - Operation Object (
OpenAPI.PathItem.Operation
) - External Document Object (
OpenAPI.ExternalDoc
) - Parameter Object (
OpenAPI.PathItem.Parameter
) - Request Body Object (
OpenAPI.Request
) - Media Type Object (
OpenAPI.Content
) - Encoding Object (
OpenAPI.Content.Encoding
) - Responses Object (
OpenAPI.Response.Map
) - Response Object (
OpenAPI.Response
) - Callback Object
- Example Object (
OpenAPI.Example
) - Link Object
- Header Object (
OpenAPI.Header
) - Tag Object (
OpenAPI.Tag
) - Reference Object (
JSONReference
) - Schema Object (
JSONSchema
) - Discriminator Object (
OpenAPI.Discriminator
) - XML Object (
OpenAPI.XML
) - Security Scheme Object (
OpenAPI.SecurityScheme
) - OAuth Flows Object (
OpenAPI.OauthFlows
) - OAuth Flow Object (
OpenAPI.OauthFlows.*
) - Security Requirement Object (
OpenAPI.Document.SecurityRequirement
)
- OpenAPI Object (
You can decode a JSON OpenAPI document (i.e. using the JSONDecoder
from Foundation library) or a YAML OpenAPI document (i.e. using the YAMLDecoder
from the Yams library) with the following code:
let decoder = ... // JSONDecoder() or YAMLDecoder()
let openAPIDoc = try decoder.decode(OpenAPI.Document, from: ...)
You can wrap any error you get back from a decoder in OpenAPI.Error
to get a friendlier human-readable description from localizedDescription
.
do {
try decoder.docode(OpenAPI.Document, from: ...)
} catch let error {
print(OpenAPI.Error(from: error).localizedDescription)
}
You can encode a JSON OpenAPI document (i.e. using the JSONEncoder
from the Foundation library) or a YAML OpenAPI document (i.e. using the YAMLEncoder
from the Yams library) with the following code:
let openAPIDoc = ...
let encoder = ... // JSONEncoder() or YAMLEncoder()
let encodedOpenAPIDoc = try encoder.encode(openAPIDoc)
The Foundation library's JSONEncoder
and JSONDecoder
do not make any guarantees about the ordering of keyed containers. This means decoding a JSON OpenAPI Document and then encoding again might result in the document's various hashed structures being in a totally different order.
If retaining order is important for your use-case, I recommend the Yams and FineJSON libraries for YAML and JSON respectively.
See VaporOpenAPI / VaporOpenAPIExample for an example of generating OpenAPI from a Vapor application's routes.
See JSONAPI+OpenAPI for an example of generating OpenAPI response schemas from JSON:API response documents.
The types used by this library largely mirror the object definitions found in the OpenAPI specification version 3.0.2. The Project Status lists each object defined by the spec and the name of the respective type in this library.
At the root there is an OpenAPI.Document
. In addition to some information that applies to the entire API, the document contains OpenAPI.Components
(essentially a dictionary of reusable components that can be referenced with JSONReferences
) and an OpenAPI.PathItem.Map
(a dictionary of routes your API defines).
Each route is an entry in the document's OpenAPI.PathItem.Map
. The keys of this dictionary are the paths for each route (i.e. /widgets
). The values of this dictionary are OpenAPI.PathItems
which define any combination of endpoints (i.e. GET
, POST
, PATCH
, etc.) that the given route supports.
Each endpoint on a route is defined by an OpenAPI.PathItem.Operation
. Among other things, this operation can specify the parameters (path, query, header, etc.), request body, and response bodies/codes supported by the given endpoint.
Request and response bodies can be defined in great detail using OpenAPI's derivative of the JSON Schema specification. This library uses the JSONSchema
type for such schema definitions.
Fundamental types are specified as JSONSchema.integer
, JSONSchema.string
, JSONSchema.boolean
, etc.
Properties are given as arguments to static constructors. By default, types are non-nullable, required, and generic.
A type can be made optional (i.e. it can be omitted) with JSONSchema.integer(required: false)
or JSONSchema.integer.optionalSchemaObject()
. A type can be made nullable with JSONSchema.number(nullable: true)
or JSONSchema.number.nullableSchemaObject()
.
A type's format can be further specified, for example JSONSchema.number(format: .double)
or JSONSchema.string(format: .dateTime)
.
You can specify a schema's allowed values (e.g. for an enumerated type) with JSONSchema.string(allowedValues: "hello", "world")
.
Each type has its own additional set of properties that can be specified. For example, integers can have a minimum value: JSONSchema.integer(minimum: (0, exclusive: true))
(where exclusive means the number must be greater than 0, not greater-than-or-equal-to 0).
Compound objects can be built with JSONSchema.array
, JSONSchema.object
, JSONSchema.all(of:)
, etc.
For example, perhaps a person is represented by the schema:
JSONSchema.object(
title: "Person",
properties: [
"first_name": .string(minLength: 2),
"last_name": .string(nullable: true),
"age": .integer,
"favorite_color": .string(allowedValues: "red", "green", "blue")
]
)
Some schemas can be easily generated from Swift types. Many of the fundamental Swift types support schema representations out-of-box.
For example, the following are true
String.openAPINode() == JSONSchema.string
Bool.openAPINode() == JSONSchema.boolean
Double.openAPINode() == JSONSchema.number(format: .double)
Float.openAPINode() == JSONSchema.number(format: .float)
...
Array
and Optional
are supported out-of-box. For example, the following are true
[String].openAPINode() == .array(items: .string)
[Int].openAPINode() == .array(items: .integer)
Int32?.openAPINode() == .integer(format: .int32, required: false)
[String?].openAPINode() == .array(items: .string(required: false))
...
Additional schema generation support can be found in the mattpolzin/OpenAPIReflection
library.
This library does not currently support file reading at all muchless following $ref
s to other files and loading them in.
This library is opinionated about a few defaults when you use the Swift types, however encoding and decoding stays true to the spec. Some key things to note:
- Within schemas,
required
is specified on the property rather than being specified on the parent object (encoding/decoding still follows the OpenAPI spec).- ex
JSONSchema.object(properties: [ "val": .string(required: true)])
is an "object" type with a required "string" type property.
- ex
- Within schemas,
required
defaults totrue
on initialization (again, encoding/decoding still follows the OpenAPI spec).- ex.
JSONSchema.string
is a required "string" type. - ex.
JSONSchema.string(required: false)
is an optional "string" type.
- ex.
See A note on dictionary ordering before deciding on an encoder/decoder to use with this library.
- openapi (
openAPIVersion
) - info
- servers
- paths
- components
- security
- tags
- externalDocs
- specification extensions
- title
- description
- termsOfService
- contact
- license
- version
- specification extensions
- name
- url
- specification extensions
- name
- url
- specification extensions
- url
- description
- variables
- specification extensions
- enum
- default
- description
- specification extensions
- schemas
- responses
- parameters
- examples
- requestBodies
- headers
- securitySchemes
- links
- callbacks
- specification extensions
- dictionary
[ ] specification extensions(not a planned addition)
- summary
- description
- servers
- parameters
- get
- put
- post
- delete
- options
- head
- patch
- trace
- specification extensions
- tags
- summary
- description
- externalDocs
- operationId
- parameters
- requestBody
- responses
- callbacks
- deprecated
- security
- servers
- specification extensions
- description
- url
- specification extensions
- name
- in (
parameterLocation
) - description
- required (part of
parameterLocation
) - deprecated
- allowEmptyValue (part of
parameterLocation
) - content (
schemaOrContent
) - schema (
schemaOrContent
)- style
- explode
- allowReserved
- example
- examples
- specification extensions
- description
- content
- required
- specification extensions
- schema
- example
- examples
- encoding
- specification extensions (
vendorExtensions
)
- contentType
- headers
- style
- explode
- allowReserved
- specification extensions
- dictionary
[ ] specification extensions(not a planned addition)
- description
- headers
- content
- links
- specification extensions
- {expression}
- specification extensions
- summary
- description
- value
- externalValue (part of
value
) - specification extensions (
vendorExtensions
)
- operationRef
- operationId
- parameters
- requestBody
- description
- server
- specification extensions
- description
- required
- deprecated
- content
- schema
- style
- explode
- allowReserved
- example
- examples
- specification extensions
- name
- description
- externalDocs
- specification extensions
- $ref
- local (same file) reference (
node
case)- encode
- decode
- dereference
- remote (different file) reference (
file
case)- encode
- decode
- dereference
- local (same file) reference (
- Mostly complete support for JSON Schema inherited keywords
- nullable
- discriminator
- readOnly (
permissions
.readOnly
case) - writeOnly (
permissions
.writeOnly
case) - xml
- externalDocs
- example
- deprecated
- specification extensions
- propertyName
- mapping
- name
- namespace
- prefix
- attribute
- wrapped
- specification extensions
- type
- description
- name (
SecurityType
.apiKey
case) - in (
location
inSecurityType
.apiKey
case) - scheme (
SecurityType
.http
case) - bearerFormat (
SecurityType
.http
case) - flows (
SecurityType
.oauth2
case) - openIdConnectUrl (
SecurityType
.openIdConnect
case) - specification extensions
- implicit
- password
- clientCredentials
- authorizationCode
- specification extensions
OpenAPI.OauthFlows.Implicit
OpenAPI.OauthFlows.Password
OpenAPI.OauthFlows.ClientCredentials
OpenAPI.OauthFlows.AuthorizationCode
- authorizationUrl
- tokenUrl
- refreshUrl
- scopes
- specification extensions
- {name} (using
JSONReferences
instead of a stringy API)