Mike Ralphson
Posted on May 23, 2017
Kin Lane, the API Evangelist, recently got in touch asking whether I had a good example of an OpenAPI 3.0.0 definition.
As part of swagger2openapi I keep up to date a conversion of the ubiquitous Swagger Petstore example API definition, and some 3.0.0 examples have recently been added to the OpenAPI specification repository, but we we both agreed I should look for something real-world which would show off the major changes between OpenAPI 2.0 (fka Swagger) and OpenAPI 3.0.0.
I turned out to be harder than I expected to find a good candidate definition, even as the maintainer of the APIs.guru collection which contains over 500 real-world APIs.
I set myself some ground rules:
- The definition had to originate in Swagger 2.0 format
- It had to be valid at source, not patched up as many APIs in APIs.guru are
- It should include oAuth authentication
- It must have
body
orformData
parameters - It must have
query
orpath
parameters
Luckily I have the metadata of all of the APIs.guru APIs extracted into a database for easy analysis. If I couldn't find an example in APIs.guru, I could always widen the search to include the 45,000-plus APIs I've indexed from GitHub and SwaggerHub.
I needed to add a couple of columns to the API metadata table, then I queried using my constraints, and ordered by size to find the most concise candidate definition.
The eventual winner was the Authentiq.Io API as mentioned on Kin's blog posting.
So, let's get stuck in, and start looking at the diff between the Swagger 2.0 original, and the converted OpenAPI 3.0.0 definition.
Metadata
-swagger: '2.0'
+openapi: 3.0.0-RC1
First things first, and we get our feet wet gently. All references to Swagger in the OpenAPI specification have been changed to OpenAPI, and that includes the swagger
property in your API definition.
While the version number is still a string, it is now semver - major.minor.patch - compatible. This means when the OpenAPI 3.0.0 specification is released, patch versions can be published to clarify wording, examples etc where this does not affect the specification itself, and minor versions can be published which add new features in a backwards-compatible way. Only breaking changes would result in an OpenAPI 4.x.x version.
Any characters after the patch version are informative only and should be ignored by tooling. In fact, tooling authors should look only at the major and minor (e.g. 3.0) versions when determining compatibility.
One other change to the info
object is that termsOfService
must now be a URL. This should not affect many APIs as this appears to always have been standard practice.
API endpoint definition
-host: connect.authentiq.io
-basePath: /
-schemes:
- - https
+servers:
+ - url: 'https://connect.authentiq.io/'
Out go separate host
, basePath
and schemes
and in comes an array of servers
each with a url
property, allowing multiple endpoints for an API. Unlike Swagger 2.0, OpenAPI 3 also supports url templating, by means of replaceable variables
(not shown here as they will not exist in converted definitions).
Content-Types
-consumes:
- - application/x-www-form-urlencoded
- - application/json
-produces:
- - application/x-www-form-urlencoded
- - application/json
- - application/problem+json
- - text/html
Out go the top-level consumes
and produces
arrays. As we will see later, each requestBody
and response
can now specify multiple content-types.
Query and Path Parameters
parameters:
- name: client_id
in: query
- type: string
description: >
A client ID obtained from the
[Dashboard](https://dashboard.authentiq.com/).
required: true
+ schema:
+ type: string
parameters
and headers
are no-longer objects which share properties like type
and items
with schema
objects, instead they include one. parameters
and headers
can optionally have a content
object instead of a schema
if their definition varies based on content-type
.
parameters
are the one area I've found where it is not possible to losslessly convert valid Swagger 2.0 definitions to OpenAPI 3.0. The array
collectionFormat
of tsv
(tab-separated values) has been dropped, and it is no longer possible to define nested separators for arrays within arrays, e.g. a|b,c|d
. If you need these features, now would be a great time to raise an issue at the OpenAPI specification repository.
FormData parameters
parameters:
- name: Authorization
in: header
description: |
HTTP Basic authorization header.
required: false
- type: string
- - name: client_id
- in: formData
- description: |
- The registered client ID.
- required: true
- type: string
This is where it gets interesting, all parameters
which are in: formData
disappear. We'll see them turn up again in a new guise shortly. The same would be true for the body
parameter.
Reference Objects
responses:
'200':
- $ref: '#/responses/Token'
+ $ref: '#/components/responses/Token'
This is an example of the restucturing that has gone on in OpenAPI 3.0.0. Schemas from /definitions
now reside under /components/schemas
and the top-level responses
object moves to /components/responses
- all $ref
s need to be updated to maintain the referential integrity of your definition.
RequestBodies
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ client_id:
+ description: |
+ The registered client ID.
+ type: string
+ client_secret:
+ description: |
+ The registered client ID secret.
+ type: string
+ format: password
Here we see the previous formData
parameters have been converted into properties of a new object held under the requestBody
property of the operation
object.
The consumes
array values become the keys of the content
object map.
Responses
responses:
'200':
description: A list of Client Objects.
- schema:
- type: array
- items:
- $ref: '#/definitions/Client'
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Client'
+ application/x-www-form-urlencoded:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Client'
Under responses
, each old produces
array value can have its own schema.
Response Headers
responses:
'201':
description: Client created
headers:
Location:
description: URL of new client resource
- type: string
+ schema:
+ type: string
As with parameters
, headers
no longer have a type
, but are defined by a schema
or content
object.
Definition Structure
parameters:
- - $ref: '#/parameters/client_id'
+ - $ref: '#/components/parameters/client_id'
Reusable parameters
have moved from the top-level parameters
object to /components/parameters
.
Security Definitions
-securityDefinitions:
- client_secret:
- description: Session management by confidential clients.
- type: oauth2
- flow: password
- tokenUrl: 'https://connect.authentiq.io/token'
- scopes:
- clients: Enable client management
+ securitySchemes:
+ client_secret:
+ description: Session management by confidential clients.
+ type: oauth2
+ flows:
+ password:
+ tokenUrl: 'https://connect.authentiq.io/token'
+ scopes:
+ clients: Enable client management
Top-level securityDefinitions
become the /components/securitySchemes
object. You can see that multiple flows
are now allowed per oAuth2 scheme.
Summary
And that's it for this example.
This walk-through of a conversion (by a work-in-progress converter, tracking a Release Candidate specification) does not show off any of the new features of OpenAPI 3.0.0 like links
and callbacks
or cookie
parameters, but hopefully shows some of the major areas of change when converting an API definition by hand, or help you find where things have moved to if you use a converter like swagger2openapi.
Also not shown are changes from the RC2 release candidate, including changes to the discriminator
property. Keep your eyes peeled for an imminent release.
As ever, if you spot anything which looks incorrect by the specification, please don't hesitate to contact me. Feedback is always gratefully received.
Posted on May 23, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.