A library containing Swift types that encode to- and decode from OpenAPI 3.0.x, OpenAPI 3.1.x, and OpenAPI 3.2.x Documents and their components.
OpenAPIKit follows semantic versioning despite the fact that the OpenAPI specificaiton does not. The following chart shows which OpenAPI specification versions and key features are supported by which OpenAPIKit versions.
| OpenAPIKit | Swift | OpenAPI v3.0, v3.1 | External Dereferencing & Sendable | OpenAPI v3.2 |
|---|---|---|---|---|
| v3.x | 5.1+ | âś… | ||
| v4.x | 5.8+ | âś… | âś… | |
| v5.x | 5.10+ | âś… | âś… | âś… |
- Usage
- Curated Integrations
- Notes
- Contributing
- Security
- Specification Coverage & Type Reference
If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the v4 migration guide.
Be aware of the changes to minimum Swift version and minimum Yams version (although Yams is only a test dependency of OpenAPIKit).
If you are migrating from OpenAPIKit 4.x to OpenAPIKit 5.x, check out the v5 migration guide.
Be aware of the change to minimum Swift version.
Most documentation will focus on what it looks like to work with the
OpenAPIKit module and OpenAPI 3.2.x documents. If you need to support OpenAPI
3.0.x documents, take a look at the section on supporting OpenAPI 3.0.x documents
4.before you get too deep into this library's docs.
Version 3.2.x of the OpenAPI Specification is backwards compatible with version 3.1.x of the specification but it adds some new features. The OpenAPIKit types support these new features regardless of what the stated Document version is, but if a Document states that it is version 3.1.x and it uses OAS 3.2.x features then OpenAPIKit will produce a warning. If you run strict validations on the document, those warnings will be errors. If you choose not to run strict validations on the document, you can handle such a document leniently.
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:
import OpenAPIKit
let decoder = ... // JSONDecoder() or YAMLDecoder()
let openAPIDoc = try decoder.decode(OpenAPI.Document.self, from: ...)OpenAPIKit adds support for new OAS versions when it has support for most or
all of the features of that OAS version. If you want to parse an OpenAPI
Document that is written in a newer version than OpenAPIKit supports and you
are asserting that the newer version is possible to parse as if it were the
pre-existing version, you can tell OpenAPIKit to parse the newer version as if
it were the older version.
You do this with userInfo passed into the Decoder you are using. For
example, to decode a hypothetical document version of "3.100.100" as if it
were version "3.1.1", set your decoder up as follows:
let userInfo = [
DocumentConfiguration.versionMapKey: ["3.100.100": OpenAPI.Document.Version.v3_1_1]
]
let decoder = ... // JSONDecoder() or YAMLDecoder()
decoder.userInfo = userInfo
let openAPIDoc = try decoder.decode(OpenAPI.Document.self, from: ...)If a version you've handled explicitly with a version map entry becomes supported natively by OpenAPIKit, the native handling will take precedence. That is, the document will stop decoding at the overridden version you specified and start decoding at its actual version. The mappings are only intended to facilitate downstream projects supporting future OpenAPI standard versions before OpenAPIKit has had time to fully implement features of those new versions.
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.decode(OpenAPI.Document.self, 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: OpenAPI.Document = ...
let encoder = ... // JSONEncoder() or YAMLEncoder()
let encodedOpenAPIDoc = try encoder.encode(openAPIDoc)Thanks to Swift's type system, the vast majority of the OpenAPI Specification is represented by the types of OpenAPIKit -- you cannot create bad OpenAPI docuements in the first place and decoding a document will fail with generally useful errors.
That being said, there are a small number of additional checks that you can perform to really put any concerns to rest.
let openAPIDoc: OpenAPI.Document = ...
// perform additional validations on the document:
try openAPIDoc.validate()You can use this same validation system to dig arbitrarily deep into an OpenAPI Document and assert things that the OpenAPI Specification does not actually mandate. For more on validation, see the OpenAPIKit Validation Documentation.
If you need to operate on OpenAPI 3.0.x documents and only 3.0.x documents, you
can use the OpenAPIKit30 module throughout your code.
However, if you need to operate on both OpenAPI 3.0.x and 3.1.x/3.2.x documents, the recommendation is to use the OpenAPIKit compatibility layer to read in a 3.0.x document and convert it to a 3.1.x or 3.2.x document so that you can use just the one set of Swift types throughout most of your program. An example of that follows.
In this example, only one file in the whole project needs to import
OpenAPIKit30 or OpenAPIKitCompat. Every other file would just import
OpenAPIKit and work with the document in the 3.2.x format.
// import OpenAPIKit30 for OpenAPI 3.0 document support
import OpenAPIKit30
// import OpenAPIKit for OpenAPI 3.2 document support
import OpenAPIKit
// import OpenAPIKitCompat to convert between the versions
import OpenAPIKitCompat
// if most of your project just works with OpenAPI v3.2, most files only need to import OpenAPIKit.
// Only in the file where you are supporting converting from OpenAPI v3.0 to v3.2 do you need the
// other two imports.
// we can support either version by attempting to parse an old version and then a new version if the old version fails
let oldDoc: OpenAPIKit30.OpenAPI.Document?
let newDoc: OpenAPIKit.OpenAPI.Document
oldDoc = try? JSONDecoder().decode(OpenAPI.Document.self, from: someFileData)
newDoc = oldDoc?.convert(to: .v3_2_0) ??
(try! JSONDecoder().decode(OpenAPI.Document.self, from: someFileData))
// ^ Here we simply fall-back to 3.2.x if loading as 3.0.x failed. You could do a more
// graceful job of this by determining up front which version to attempt to load or by
// holding onto errors for each decode attempt so you can tell the user why the document
// failed to decode as neither 3.0.x nor 3.2.x if it fails in both cases.You could also call oldDoc?.convert(to: .v3_1_4) if you specifically wanted to
work against a 3.1.x document for whatever reason (possibly because you wanted
to reserialize the document at a 3.1.x version).
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 different order.
If retaining order is important for your use-case, I recommend the Yams for both YAML and JSON documents. This will get you JSON decoding as well as YAML decoding since YAML is a superset of JSON. You can encode with the Foundation JSON library still or use another third party JSON library like FineJSON.
The Foundation JSON encoding and decoding will be the most stable and battle-tested option with Yams as a pretty well established and stable option as well. FineJSON is lesser used (to my knowledge) but I have had success with it in the past.
The types used by this library largely mirror the object definitions found in
the OpenAPI specification version 3.2.0
(OpenAPIKit module) and version 3.0.4
(OpenAPIKit30 module). The Project Status lists each object
defined by the spec and the name of the respective type in this library. The
project status page currently focuses on OpenAPI 3.2.x but for the purposes of
determining what things are named and what is supported you can mostly infer the
status of the OpenAPI 3.0.x support as well.
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 OpenAPI.References) 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. In
addition to accessing endpoints on a path item under the name of the method
(.get, .post, etc.), you can get an array of pairs matching endpoint methods
to operations with the .endpoints method on PathItem.
Each endpoint on a route is defined by an OpenAPI.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.
Schema attributes are given as arguments to static constructors. By default, schemas are non-nullable, required, and generic. The below examples are not comprehensive and you can pass any number of the optional attributes to the static constructors as arguments.
A schema can be made optional (i.e. it can be omitted) with
JSONSchema.integer(required: false) or an existing schema can be asked for an
optionalSchemaObject().
A schema can be made nullable with JSONSchema.number(nullable: true) or an
existing schema can be asked for a nullableSchemaObject().
Nullability highlights an important decision OpenAPIKit makes. The JSON Schema
specification that dictates how OpenAPI v3.2 documents encode nullability
states that a nullable property is encoded as having the null type in addition
to whatever other type(s) it has. So in OpenAPIKit you set nullability as a
property of a schema, but when encoded/decoded it will represent the inclusion
of absence of null in the list of types of the schema. If you are using the
OpenAPIKit30 module then nullability is encoded as a nullable property per
the OpenAPI 3.0.x specification.
Some types of schemas can be further specialized with a format. 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 of schema 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)). exclusive: true in this
context means the number must be strictly greater than 0 whereas exclusive: false means the number must be 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")
]
)Take a look at the OpenAPIKit Schema Object documentation for more information.
The
OpenAPI.Reference
type represents the OpenAPI specification's reference support that is
essentially just JSON Reference specification compliant but with the ability to
override summaries and descriptions at the reference site where appropriate.
For details on the underlying reference support, see the next section on the
JSONReference type.
The
JSONReference
type allows you to work with OpenAPIDocuments that store some of their
information in the shared Components Object dictionary or even external files.
Only documents where all references point to the Components Object can be
dereferenced currently, but you can encode and decode all references.
You can create an external reference with JSONReference.external(URL).
Internal references usually refer to an object in the Components Object
dictionary and are constructed with JSONReference.component(named:). If you
need to refer to something in the current file but not in the Components Object,
you can use JSONReference.internal(path:).
You can check whether a given JSONReference exists in the Components Object
with document.components.contains(). You can access a referenced object in the
Components Object with document.components[reference].
References can be created from the Components Object with
document.components.reference(named:ofType:). This method will throw an error
if the given component does not exist in the ComponentsObject.
You can use document.components.lookup() or the Components type's
subscript to turn an Either containing either a reference or a component
into an optional value of that component's type (having either pulled it out of
the Either or looked it up in the Components Object). The lookup() method
throws when it can't find an item whereas subscript returns nil.
For example,
let apiDoc: OpenAPI.Document = ...
let addBooksPath = apiDoc.paths["/cloudloading/addBook"]
let addBooksParameters: [OpenAPI.Parameter]? = addBooksPath?.parameters.compactMap { apiDoc.components[$0] }Note that this looks a component up in the Components Object but it does not transform it into an entirely derefernced object in the same way as is described below in the Dereferencing & Resolving section.
In the OpenAPI specifcation, a security requirement (like can be found on the root Document or on Operations) is a dictionary where each key is the name of a security scheme found in the Components Object and each value is an array of applicable scopes (which is of course only a non-empty array when the security scheme type is one for which "scopes" are relevant).
OpenAPIKit defines the
SecurityRequirement
typealias as a dictionary with JSONReference keys; These references point to
the Components Object and provide a slightly stronger contract than the String
values required by the OpenAPI specification. Naturally, these are encoded to
JSON/YAML as String values rather than JSON References to maintain compliance
with the OpenAPI Specification.
To give an example, let's say you want to describe OAuth 2.0 authentication via the implicit flow. First, define a Security Scheme:
let oauthScheme = OpenAPI.SecurityScheme.oauth2(
flows: .init(
implicit: .init(
authorizationUrl: URL(string: "https://my-api.com/oauth2/auth")!,
scopes: ["read:widget": "read widget"]
)
)
)Next, store it in your OpenAPI document's Components Object (which likely has other entries but we'll only specify the security schemes for this example):
let components = OpenAPI.Components(
securitySchemes: ["implicit-oauth": oauthScheme]
)Finally, your OpenAPI Document should use the Components Object we just created and also reference the OAuth implicit scheme via an internal JSON reference:
let document = OpenAPI.Document(
info: .init(title: "API", version: "1.0"),
servers: [],
paths: [:],
components: components,
security: [[.component( named: "implicit-oauth"): ["read:widget"]]]
)If your API supports multiple alternative authentication strategies (only one of which needs to be used), you might have additional entries in your Document's Security array:
let document = OpenAPI.Document(
info: .init(title: "API", version: "1.0"),
servers: [],
paths: [:],
components: components,
security: [
[.component( named: "implicit-oauth"): ["read:widget"]],
[.component( named: "auth-code-oauth"): ["read:widget"]]
]
)Reusable OpenAPI Document components are supported by the OpenAPI.Components
type. You find components at the components property of an OpenAPI.Document.
Each component is stored in a dictionary of components with the same type --
schemas, responses, parameters, and so on. The keys components are stored under
are arbitrary string values that can be used to refer to the component from
elsewhere in the document.
If you want to use a Component entry in an OpenAPI Document (e.g. serialize a
reference to the component in a resulting document), you will usually create a
reference using the component(named:) constructor. For example:
let pathItem : OpenAPI.Reference<OpenAPI.PathItem> = .component(named: "path1")You have a few options for retrieving values from the Components.
First, you can use subscript access. let response : OpenAPI.Response = document.components[.component(named: "response1")] will retrieve a Response
under the key "response1" if found and otherwise it will result in nil.
Second, you can use the lookup() function. This function operates like
subscript access but it throws an error if the requested component cannot be
found.
Lastly, you can use lookupOnce(). This is like lookup() but it will not
follow references so if the Component at the given key is itself a reference to
another component (for example), lookupOnce() gives back the reference whereas
lookup() and subscript access follow the reference to fully resolve it.
Many OpenAPIKit types support
Specification Extensions.
As described in the OpenAPI Specification, these extensions must be objects that
are keyed with the prefix "x-". For example, a property named "specialProperty"
on the root OpenAPI Object (OpenAPI.Document) is invalid but the property
"x-specialProperty" is a valid specification extension.
You can get or set specification extensions via the
vendorExtensions
property on any object that supports this feature. The keys are Strings
beginning with the aforementioned "x-" prefix and the values are AnyCodable.
If you set an extension without using the "x-" prefix, the prefix will be added
upon encoding.
If you wish to disable decoding/encoding of vendor extensions for performance
reasons, you can configure the Encoder and Decoder using their userInfo:
let userInfo = [VendorExtensionsConfiguration.enabledKey: false]
let encoder = JSONEncoder()
encoder.userInfo = userInfo
let decoder = JSONDecoder()
decoder.userInfo = userInfoOpenAPIKit uses the AnyCodable type for vendor extensions and constructing
examples for JSON Schemas. OpenAPIKit's AnyCodable type is an adaptation of
the Flight School library that can be found
here.
AnyCodable can be constructed explicitly or from many types of literals. The
following are all valid.
var document = OpenAPI.Document(...)
document.vendorExtensions["x-specialProperty1"] = true
document.vendorExtensions["x-specialProperty2"] = "hello world"
document.vendorExtensions["x-specialProperty3"] = AnyCodable("hello world")
document.vendorExtensions["x-specialProperty4"] = AnyCodable(["hello", "world"])
document.vendorExtensions["x-specialProperty5"] = AnyCodable(["hello": "world"])It is important to note that AnyCodable wraps Swift types in a way that keeps
track of the Swift type used to construct it as much as possible, but if you
encode an AnyCodable and then decode that result, the decoded value may not
always be the same as the pre-encoded value started out. This is because many
Swift types will encode to "stringy" values and then decode as simply String
values. There are two ways to cope with this:
- When adding stringy values to structures that will be passed to
AnyCodable, you can explicitly turn them intoStrings. For example, you can useURL(...).absoluteStringboth to specify you want the absolute value of the URL encoded and also to turn it into aStringup front. - When comparing
AnyCodablevalues that have passed through a full encode/decode cycle, you can compare thedescriptionof the twoAnyCodablevalues. This stringy result is more likely to compare equivalently.
Keep in mind, this issue only occurs when you are comparing value a and value
b for equality given that b is a after being encoded and then subsequently
decoded.
The other sure-fire way to handle this (if you need encode-decode equality, not
just equivalence) is to make sure you run both values being compared through
encoding. For example, you might use the following function which doesn't even
care if the input is AnyCodable or not:
func encodedEqual<A: Codable, B: Codable>(_ a: A, _ b: B) throws -> Bool {
let a = try JSONEncoder().encode(a)
let b = try JSONEncoder().encode(b)
return a == b
}For example, the result of the following is true:
try encodeEqual(URL(string: "https://website.com"), AnyCodable(URL(string: "https://website.com")))External dereferencing does not resolve any local (internal) references, it just loads external references into the Document. It does this by storing any loaded externally referenced objects in the Components Object and transforming the reference being resolved from an external reference to an internal one. That way, you can always run internal dereferencing as a second step if you want a fully dereferenced document, but if you simply wanted to load additional referenced files then you can stop after external dereferencing.
OpenAPIKit leaves it to you to decide how to load external files and where to
store the results in the Components Object. It does this by requiring that you
provide an implementation of the
ExternalLoader
protocol. You provide a load function and a componentKey function, both of
which accept as input the URL to load. A simple mock example implementation
from the OpenAPIKit tests will go a long way to showing how the ExternalLoader
can be set up:
struct ExampleLoader: ExternalLoader {
typealias Message = Void
static func load<T>(_ url: URL) async throws -> (T, [Message]) where T : Decodable {
// load data from file, perhaps. we will just mock that up for the example:
let data = try await mockData(componentKey(type: T.self, at: url))
// We use the YAML decoder purely for order-stability.
let decoded = try YAMLDecoder().decode(T.self, from: data)
let finished: T
// while unnecessary, a loader may likely want to attatch some extra info
// to keep track of where a reference was loaded from. This example
shows
// the strategy of using vendor extensions.
if var extendable = decoded as? VendorExtendable {
extendable.vendorExtensions["x-source-url"] = AnyCodable(url)
finished = extendable as! T
} else {
finished = decoded
}
return (finished, [])
}
static func componentKey<T>(type: T.Type, at url: URL) throws -> OpenAPIKit.OpenAPI.ComponentKey {
// do anything you want here to determine what key the new component should be stored at.
//
// for the example, we will just transform the URL path into a valid components key:
let urlString = url.pathComponents
.joined(separator: "_")
.replacingOccurrences(of: ".", with: "_")
return try .forceInit(rawValue: urlString)
}
}Once you have an ExternalLoader, you can call an OpenAPI.Document's
externallyDereference() method to externally dereference it. You get to choose
whether to only load references to a certain depth or to fully resolve
references until you run out of them; any given referenced document may itself
contain references and these references may point back to things loaded into the
Document previously so dereferencing is done recursively up to a given depth (or
until fully dereferenced if you use the .full depth).
If you have some information that you want to pass back to yourself from the
load() function, you can specify any type you want as the Message type and
return any number of messages from each load() function execution. These
messages could be warnings, additional information about the files that each
newly loaded Component came from, etc. If you want to tie some information about
file loading to new Components in your messages, you can use the
componentKey() function to get the key the new Component will be found under
once external dereferencing is complete.
In addition to looking something up in the
Components
object, you can entirely derefererence many OpenAPIKit types. A dereferenced
type has had all of its references looked up (and all of its properties'
references, all the way down).
You use a value's
dereferenced(in:)
method to fully dereference it.
You can even dereference the whole document with the OpenAPI.Document
locallyDereferenced() method. As the name implies, you can only derefence
whole documents that have previously been externally dereferenced (or documents
contained within one file) -- that is, all references are "local". Specifically,
all references must be located within the document's Components Object. External
dereferencing is done as a separeate step, but you can first dereference
externally and then dereference internally if you'd like to perform both.
Unlike what happens when you lookup an individual component using the lookup()
method on Components, dereferencing a whole OpenAPI.Document will result in
type-level changes that guarantee all references are removed.
OpenAPI.Document's locallyDereferenced() method returns a
DereferencedDocument which exposes DereferencedPathItems which have
DereferencedParameters and DereferencedOperations and so on.
Anywhere that a type would have had either a reference or a component, the
dereferenced variety will simply have the component. For example, PathItem has
an array of parameters, each of which is Either<OpenAPI.Reference<Parameter>, Parameter> whereas a DereferencedPathItem has an array of
DereferencedParameters. The dereferenced variant of each type exposes all the
same properties and you can get at the underlying OpenAPI type via an
underlying{TypeName} property. This can make for a much more convenient way to
traverse a document because you don't need to check for or look up references
anywhere the OpenAPI Specification allows them.
For all dereferenced types, dereferencing will store a new vendor extension on
the dereferenced value to keep track of the Component Object name the value used
to be referenced at. This vendor extension is a string value with the
x-component-name key.
You can take things a step further and resolve the document. Calling
resolved() on a DereferencedDocument will produce a canonical form of an
OpenAPI.Document. The ResolvedRoutes and ResolvedEndpoints that the
ResolvedDocument exposes collect all relevant information from the whole
document into themselves. For example, a ResolvedEndpoint knows what servers
it can be used on, what path it is located at, and which parameters it supports
(even if some of those parameters were defined in an OpenAPI.Operation and
others were defined in the containing OpenAPI.PathItem).
If your end goal is to analyze the OpenAPI Document or generate something
entirely new (like code) from it, the ResolvedDocument is by far more
convenient to traverse and query than the original OpenAPI.Document. The
downside is, there is not currently support for mutating the ResolvedDocument
and then turning it back into an OpenAPI.Document to encode it.
let document: OpenAPI.Document = ...
let resolvedDocument = try document
.locallyDereferenced()
.resolved()
for endpoint in resolvedDocument.endpoints {
// The description found on the PathItem containing the Operation defining this endpoint:
let routeDescription = endpoint.routeDescription
// The description found directly on the Operation defining this endpoint:
let endpointDescription = endpoint.endpointDescription
// The path, which in the OpenAPI.Document is the key of the dictionary containing
// the PathItem under which the Operation for this endpoint lives:
let path = endpoint.path
// The method, which in the OpenAPI.Document is the way you access the Operation for
// this endpoint on the PathItem (GET, PATCH, etc.):
let httpMethod = endpoint.method
// All parameters defined for the Operation _or_ the PathItem containing it:
let parameters = endpoint.parameters
// Per the specification, this is
// 1. the list of servers defined on the Operation if one is given.
// 2. the list of servers defined on the PathItem if one is given _and_
// no list was found on the Operation.
// 3. the list of servers defined on the Document if no list was found on
// the Operation _or_ the PathItem.
let servers = endpoint.servers
// and many more properties...
}Following is a short list of integrations that might be immediately useful or just serve as examples of ways that OpenAPIKit can be used to harness the power of the OpenAPI specification.
If you have a library you would like to propose for this section, please create a pull request and explain a bit about your project.
The swift-openapi-generator uses OpenAPIKit under the hood and generates Swift code for interfacing with APIs that have OpenAPI descriptions.
VaporOpenAPI / VaporOpenAPIExample provide an example of generating OpenAPI from a Vapor application's routes.
JSONAPI+OpenAPI is a library that generates OpenAPI schemas from JSON:API types. The library has some rudimentary and experimental support for going the other direction and generating Swift types that represent JSON:API resources described by OpenAPI documentation.
The Swift Package Registry API Docs define the OpenAPI documentation for the Swift Package Registry standard using declarative Swift code and OpenAPIKit. This project also provides a useful example of producing a user-friendly ReDoc web interface to the OpenAPI documentation after encoding it as YAML.
OpenAPIDiff is a library and a CLI that implements semantic diffing; that is, rather than just comparing two OpenAPI documents line-by-line for textual differences, it parses the documents and describes the differences in the two OpenAPI ASTs.
This library does not handle reading files from disk or otherwise. You must
read OpenAPI documentation into Data or String (depending on the decoder you
want to use) and then decode using OpenAPIKit's Decodable conformances.
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,
requiredis 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,
requireddefaults totrueon initialization (again, encoding/decoding still follows the OpenAPI spec).- ex.
JSONSchema.stringis 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.
Contributions to OpenAPIKit are welcome and appreciated! The project is mostly maintained by one person which means additional contributors have a huge impact on how much gets done how quickly.
Please see the Contribution Guidelines for a few brief notes on contributing the the project.
The OpenAPIKit project takes code security seriously. As part of the Swift Server Workground incubation program, this project follows a shared set of standards around receiving, reporting, and reacting to security vulnerabilies.
Please see Security for information on how to report vulnerabilities to the OpenAPIKit project and what to expect after you do.
Please do not report security vulnerabilities via GitHub issues.
For a full list of OpenAPI Specification types annotated with whether OpenAPIKit supports them and relevant translations to OpenAPIKit types, see the Specification Coverage documentation. For detailed information on the OpenAPIKit types, see the full type documentation.