Comparing OpenAPI/Swagger 2.0 and 3.0.0-rc1

mikeralphson

Mike Ralphson

Posted on May 23, 2017

Comparing OpenAPI/Swagger 2.0 and 3.0.0-rc1

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 or formData parameters
  • It must have query or path 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
Enter fullscreen mode Exit fullscreen mode

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/'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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 $refs 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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
mikeralphson
Mike Ralphson

Posted on May 23, 2017

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related