Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc44467
move OAS 3.1.2 support to the base version enum
mattpolzin Sep 20, 2025
f6671fa
Add support for OAS 3.2.x version handling and the Tag summary field
mattpolzin Oct 13, 2025
eb8cf76
Add conditional warnings to validator collection. Test the first such…
mattpolzin Oct 13, 2025
fd04b6d
clean up errors that have always had extra punctuation
mattpolzin Oct 19, 2025
c7a7220
add Tag support for the parent field
mattpolzin Oct 19, 2025
b0353de
Add Tag support for the kind field
mattpolzin Oct 19, 2025
2622494
drop Swift support prior to 5.10
mattpolzin Oct 20, 2025
5ab7759
add swift 6.2 CI into the matrix
mattpolzin Oct 20, 2025
94f9f20
Merge pull request #429 from mattpolzin/feature/425/oas3-2-0
mattpolzin Oct 20, 2025
4067f83
support and prefer new official yaml media type
mattpolzin Oct 20, 2025
ef352d3
Merge pull request #430 from mattpolzin/feature/423/yaml-media-type
mattpolzin Oct 20, 2025
11afd7b
Merge branch 'main' into release/5_0
mattpolzin Oct 22, 2025
efa684c
use swift tools version that aligns with minimum supported swift version
mattpolzin Oct 23, 2025
0710155
Merge pull request #435 from mattpolzin/bump-package-tools-version
mattpolzin Oct 23, 2025
74e8bb7
Add new HTTP QUERY method support
mattpolzin Oct 20, 2025
da79634
Add OAS 3.2.0 warning for PathItems with query endpoints
mattpolzin Oct 20, 2025
4b75af7
Add additionalOperations to PathItem type. Expand HttpMethod type to …
mattpolzin Oct 21, 2025
832263a
catch the spec coverage list up
mattpolzin Oct 24, 2025
0550e8a
update README and begin writing migration guide
mattpolzin Oct 24, 2025
495a63d
add a few more tidbits to the docs for the HttpMethod type
mattpolzin Oct 24, 2025
e285b2a
Merge pull request #432 from mattpolzin/feature/431/new-http-methods
mattpolzin Oct 24, 2025
38b3deb
support oas 3.2.0 $self property of the Document object
mattpolzin Oct 27, 2025
8508f98
Merge pull request #440 from mattpolzin/feature/436/document-changes
mattpolzin Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ jobs:
fail-fast: false
matrix:
image:
- swift:5.8-focal
- swift:5.8-jammy
- swift:5.9-focal
- swift:5.9-jammy
- swift:5.10-focal
- swift:5.10-jammy
- swift:6.0-focal
Expand All @@ -25,6 +21,8 @@ jobs:
- swift:6.1-focal
- swift:6.1-jammy
- swift:6.1-noble
- swift:6.2-jammy
- swift:6.2-noble
- swiftlang/swift:nightly-focal
- swiftlang/swift:nightly-jammy
container: ${{ matrix.image }}
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// swift-tools-version: 5.8
// swift-tools-version: 5.10

import PackageDescription

let package = Package(
name: "OpenAPIKit",
platforms: [
.macOS(.v10_15),
.iOS(.v11)
.iOS(.v12)
],
products: [
.library(
Expand Down
83 changes: 34 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 5.8+](http://img.shields.io/badge/Swift-5.8+-blue.svg)](https://swift.org)
[![sswg:sandbox|94x20](https://img.shields.io/badge/sswg-sandbox-lightgrey.svg)](https://github.com/swift-server/sswg/blob/master/process/incubation.md#sandbox-level) [![Swift 5.10+](http://img.shields.io/badge/Swift-5.10+-blue.svg)](https://swift.org)

[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) ![Tests](https://github.com/mattpolzin/OpenAPIKit/actions/workflows/tests.yml/badge.svg?branch=main)

# OpenAPIKit <!-- omit in toc -->

A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4.html) and [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.1.html) Documents and their components.
A library containing Swift types that encode to- and decode from [OpenAPI 3.0.x](https://spec.openapis.org/oas/v3.0.4.html), [OpenAPI 3.1.x](https://spec.openapis.org/oas/v3.1.2.html), and [OpenAPI 3.2.x](https://spec.openapis.org/oas/v3.2.0.html) 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 | OpenAPI v3.1 | External Dereferencing & Sendable |
|------------|-------|--------------|--------------|-----------------------------------|
| v2.x | 5.1+ | ✅ | | |
| v3.x | 5.1+ | ✅ | ✅ | |
| v4.x | 5.8+ | ✅ | ✅ | ✅ |
| OpenAPIKit | Swift | OpenAPI v3.0, v3.1 | External Dereferencing & Sendable | OpenAPI v3.2 |
|------------|-------|--------------------|-----------------------------------|--------------|
| v3.x | 5.1+ | ✅ | | |
| v4.x | 5.8+ | ✅ | ✅ | |
| v4.x | 5.8+ | ✅ | ✅ | ✅ |

- [Usage](#usage)
- [Migration](#migration)
- [1.x to 2.x](#1.x-to-2.x)
- [2.x to 3.x](#2.x-to-3.x)
- [3.x to 4.x](#3.x-to-4.x)
- [Older Versions](#older-versions)
- [3.x to 4.x](#3x-to-4x)
- [4.x to 5.x](#4x-to-5x)
- [Decoding OpenAPI Documents](#decoding-openapi-documents)
- [Decoding Errors](#decoding-errors)
- [Encoding OpenAPI Documents](#encoding-openapi-documents)
Expand Down Expand Up @@ -47,40 +47,25 @@ OpenAPIKit follows semantic versioning despite the fact that the OpenAPI specifi
## Usage

### Migration
#### 1.x to 2.x
If you are migrating from OpenAPIKit 1.x to OpenAPIKit 2.x, check out the [v2 migration guide](./documentation/v2_migration_guide.md).
#### Older Versions
- [`1.x` to `2.x`](./documentation/migration_guides/v2_migration_guide.md)
- [`2.x` to `3.x`](./documentation/migration_guides/v3_migration_guide.md)

#### 2.x to 3.x
If you are migrating from OpenAPIKit 2.x to OpenAPIKit 3.x, check out the [v3 migration guide](./documentation/v3_migration_guide.md).

You will need to start being explicit about which of the two new modules you want to use in your project: `OpenAPIKit` (now supports OpenAPI spec v3.1) and/or `OpenAPIKit30` (continues to support OpenAPI spec v3.0 like the previous versions of OpenAPIKit did).

In package manifests, dependencies will be one of:
```
// v3.0 of spec:
dependencies: [.product(name: "OpenAPIKit30", package: "OpenAPIKit")]

// v3.1 of spec:
dependencies: [.product(name: "OpenAPIKit", package: "OpenAPIKit")]
```

Your imports need to be specific as well:
```swift
// v3.0 of spec:
import OpenAPIKit30
#### 3.x to 4.x
If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the [v4 migration guide](./documentation/migration_guides/v4_migration_guide.md).

// v3.1 of spec:
import OpenAPIKit
```
Be aware of the changes to minimum Swift version and minimum Yams version (although Yams is only a test dependency of OpenAPIKit).

It is recommended that you build your project against the `OpenAPIKit` module and only use `OpenAPIKit30` to support reading OpenAPI 3.0.x documents in and then [converting them](#supporting-openapi-30x-documents) to OpenAPI 3.1.x documents. The situation not supported yet by this strategy is where you need to write out an OpenAPI 3.0.x document (as opposed to 3.1.x). That is a planned feature but it has not yet been implemented. If your use-case benefits from reading in an OpenAPI 3.0.x document and also writing out an OpenAPI 3.0.x document then you can operate entirely against the `OpenAPIKit30` module.
#### 4.x to 5.x
If you are migrating from OpenAPIKit 4.x to OpenAPIKit 5.x, check out the [v5 migration guide](./documentation/migration_guides/v5_migration_guide.md).

#### 3.x to 4.x
If you are migrating from OpenAPIKit 3.x to OpenAPIKit 4.x, check out the [v4 migration guide](./documentation/v4_migration_guide.md).
Be aware of the change to minimum Swift version.

### Decoding OpenAPI Documents

Most documentation will focus on what it looks like to work with the `OpenAPIKit` module and OpenAPI 3.1.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](#supporting-openapi-30x-documents) before you get too deep into this library's docs.
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](#supporting-openapi-30x-documents) 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**](https://github.com/jpsim/Yams) library) with the following code:
```swift
Expand Down Expand Up @@ -148,21 +133,21 @@ You can use this same validation system to dig arbitrarily deep into an OpenAPI
### Supporting OpenAPI 3.0.x Documents
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 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 document so that you can use just the one set of Swift types throughout most of your program. An example of that follows.
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.1.x format.
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.

#### Converting from 3.0.x to 3.1.x
#### Converting from 3.0.x to 3.2.x
```swift
// import OpenAPIKit30 for OpenAPI 3.0 document support
import OpenAPIKit30
// import OpenAPIKit for OpenAPI 3.1 document support
// 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.1, most files only need to import OpenAPIKit.
// Only in the file where you are supporting converting from OpenAPI v3.0 to v3.1 do you need the
// 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
Expand All @@ -171,12 +156,12 @@ let newDoc: OpenAPIKit.OpenAPI.Document

oldDoc = try? JSONDecoder().decode(OpenAPI.Document.self, from: someFileData)

newDoc = oldDoc?.convert(to: .v3_1_1) ??
newDoc = oldDoc?.convert(to: .v3_2_0) ??
(try! JSONDecoder().decode(OpenAPI.Document.self, from: someFileData))
// ^ Here we simply fall-back to 3.1.x if loading as 3.0.x failed. You could do a more
// ^ 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.1.x if it fails in both cases.
// failed to decode as neither 3.0.x nor 3.2.x if it fails in both cases.
```

### A note on dictionary ordering
Expand All @@ -187,7 +172,7 @@ If retaining order is important for your use-case, I recommend the [**Yams**](ht
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.

### OpenAPI Document structure
The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.1.1](https://spec.openapis.org/oas/v3.1.1.html) (`OpenAPIKit` module) and [version 3.0.4](https://spec.openapis.org/oas/v3.0.4.html) (`OpenAPIKit30` module). The [Project Status](#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.1.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.
The types used by this library largely mirror the object definitions found in the OpenAPI specification [version 3.2.0](https://spec.openapis.org/oas/v3.2.0.html) (`OpenAPIKit` module) and [version 3.0.4](https://spec.openapis.org/oas/v3.0.4.html) (`OpenAPIKit30` module). The [Project Status](#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.

#### Document Root
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).
Expand All @@ -210,7 +195,7 @@ A schema can be made **optional** (i.e. it can be omitted) with `JSONSchema.inte

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.1 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 `type`s 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.
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 `type`s 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)`.

Expand Down Expand Up @@ -311,7 +296,7 @@ let document = OpenAPI.Document(
```

#### Specification Extensions
Many OpenAPIKit types support [Specification Extensions](https://spec.openapis.org/oas/v3.1.1.html#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.
Many OpenAPIKit types support [Specification Extensions](https://spec.openapis.org/oas/v3.2.0.html#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`](https://mattpolzin.github.io/OpenAPIKit/documentation/openapikit/vendorextendable/vendorextensions-swift.property) 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.

Expand Down
Loading