Alex's Notes

OpenAPI (and Swagger)

Notes from a LinkedIn Learning Course on Specifying APIs with Swagger tooling and the Open API standard.


Key Links

Swagger

OpenAPI Spec 3.0

OpenAPI data types

OpenAPI schema

-Learn the spec by drilling down into the nested objects in the schema on this page.


Swagger is a toolset and structured approach for creating API designs, documentation, and code through the API lifecycle.

The Open API specification is a standard format for metadata used to define RESTful services. The metadata is machine readable.

The Swagger tools include open source tools like Swagger Editor, Swagger UI and Swagger Codegen. The pro tools are made for teams, and publishing, Swagger Hub and Swagger Inspector.

Swagger Editor is an IDE for API definition files. demo

Swagger UI consumes those definition files to build interactive documentation. demo

Swagger Codegen builds SDKs from API definitions. For demo see the top menu of the editor demo above.

The OpenAPI Specification (OAS) defines a standard, programming-language-agnostic interface description for REST APIs.

The problem it’s solving is inconsistent ways of describing APIs makes integration harder. With an industry standard, we can now share our API’s interface with people and machines in a predictable way.

To install Swagger you download the source from github, put them somewhere and then point a server at them. EG the node server could be executed in bash like this:

alias swagger="http-server ~/Apps/swagger-editor/ -a 127.0.0.1 -p 8080"
alias swaggerui="http-server ~/Apps/swagger-ui/dist/ -a 127.0.0.1 -p 8081"

To define an Open API spec with Swagger you need to know the specification

we start with the minimal requirements in the spec. We define the standard of the spec we’re using, and some info and paths:

openapi: 3.0.0
info:
  title: My First API
  version: 1.0.0
paths:
  /product:
    get:
      responses:
	200:
	  description: This is a list of products in the catalogue
	  content:
	    application/json:
	      schema:
		type: array
		items:
		  properties:
		    id:
		      type: integer
		      example: 300
		    name:
		      type: string
		      example: Lemon Water

The most interesting thing here is the paths. The paths list the routes of the API. Each path object has a series of http methods that it supports. Each method has a series of responses. Those responses contain a description of their purpose and then an object describing their media type and schema.

So the tree goes like this:

  • paths (eg /product)

  • methods (eg get, post)

  • responses

  • description followed by content

  • media type eg application/json

  • schema of the response

We can use tags to organize our routes. We define them just ahead of the paths like this:

tags:
  - name: Article
    description: Articles for the Blog
paths:
  /article:
    get:
      tags:
	- Article
      responses:
	...

The result will be organized blocks of routes.

The OpenAPI spec in more depth

There are seven sections (or objects) that can be contained at the root level:

  1. info (required)

    Provides metadata about the API like description, terms of service, contact info, license, version.

  2. servers

    Provides metadata about the servers hosting the API. Used to provide urls for the different environments, eg dev, UAT, prod.

  3. security

    Describes how the API is secured by specifying auth flows, keys etc.

  4. paths (required)

    Describes the endpoints of hte API and the different operations that can be performed on those endpoints. The largest part of the spec.

  5. tags

    Provide tags to group the API operations logically.

  6. externaldocs

    Supply additional metadata and links to external API documentation.

  7. components

    Defines a set of reusable objects that can be referenced throughout the API. EG parameters, response bodies. Prevents repeated defintions.

Within these, objects can be nested to build out the full API spec.

Most commonly you’ll encounter these written in yaml, but JSON is also supported.

The paths and components objects are the two most important. Most of the work is done here.

More details can be found by reading the spec on github.

Drilling down into the most important objects in the openapi schema, the schema object itself is really important. Get to know it well, it is described here

Query Parameters

Typically APIs will include paging and other query parameters.

Parameters like this go under the http verb in swagger.

We can specify an array of parameters with the - in: <type> where type is either query (query parameter), header, path, or cookie.

paths:
  /product:
    get:
      parameters:
	- in: query
	  name: pageNumber
	  description: Page number to return
	  required: false
	  schema:
	    type: integer
	    example: 1
	- in: query
	  name: pageSize
	  description: Number of items in page
	  required: false
	  schema:
	    type: integer
	    example: 10
	    maximum: 100

Path/Template Parameters

REST APIs are typically based around path parameters like /product/{productID}

These are listed in the OpenAPI spec as separate paths (so you’d need both /product and /product/{productID} in your paths.

Then you can specify the path parameter sa follows, note that the param must be required, and the name must match the name in curly braces in the path itself. Responses are required.

This example also includes an example of a header parameter

An example:

/product/{productId}:
  get:
    tags:
      - Product
    parameters:
      - in: path
	name: productId
	required: true
	schema:
	  type: integer
	  description: The id itself
	  example: 3
      - in: header
	name: custom-security-header
	required: false
	schema:
	  type: integer
	  example: 12312-13114141-141414141
    responses:
      200:
	description: This is a product item in the catalogue
	content:
	  application/json:
	    schema:
	      type: object
	      properties:
		id:
		  type: integer
		  example: 300
		name:
		  type: string
		  example: Lemon Water

Request Bodies

It is straightforward to define the required request body in operations like POST, here’s an example:

/product:
  post:
    description: Add a product to the catalogue
    tags:
      - Product
    requestBody:
      content:
	application/json:
	  schema:
	    type: object
	    properties:
	      id:
		type: integer
		example: 300
	      name:
		type: string
		example: Lemon Water
    responses:
      200:
	description: The product has been created.

Components

Already we see that we’re repeating ourselves a lot, every time a product is mentioned it’s the same object essentially. We can stay DRY by using components in the OpenAPI 3.0 spec.

These describe re-usable components like parameters or schemas that can be referred to in our definition. They sit at the top level of the document tree.

Within the components object we can specify the schemas of each component, for example:

components:
  schemas:
    Product:
      type: object
      required:
	- name
      properties:
	id:
	  type: integer
	  example: 300
	name:
	  type: string
	  example: Lemon Water

Note that we’ve added validation now, we can specify in the required array which properties are required when the object is used.

Then you can reference the component by its name inside the schema (or items of an array) using the $ref: property like this:

responses:
  200:
    description: This is a product item in the catalogue
    content:
      application/json:
	schema:
	  $ref: '#/components/schemas/Product'
      application/xml:
	schema:
	  $ref: '#/components/schemas/Product'

Now you can update the product schema in one place. and support multiple media types.

You can define more than just schema components. You can define standard responses, parameters, headers, requestBodies, examples, securitySchemes, links, and callbacks.

EG for responses, you can define standard errors and then reuse them.

components:
  responses:
    500ApiError:
      description: This is an unexpected error
      content:
	application/json:
	  schema:
	    type: object
	    properties:
	      statusCode:
		type: string
		example: 500
	      message:
		type: string
		example: This is an error

Then you can say:

responses:
  500:
    $ref: '#components/responses/500ApiError'

Parameters are also highly reusable so make sense to include in components:

  /product:
    get:
      parameters:
	- $ref: '#/components/parameters/PageSize'
	- $ref: '#/components/parameters/PageNumber'

components:
  parameters:
    PageNumber:
      in: query
      name: pageNumber
      description: Page number to return
      required: false
      schema:
	type: integer
	example: 1
    PageSize:
      in: query
      name: pageSize
      description: Number of items in page
      required: false
      schema:
	type: integer
	example: 10
	maximum: 100

Docs Through Swagger UI

Swagger UI download has a dist folder. To serve your yaml definition in the UI you need to:

  • make a copy of the dist folder. Rename it to something descriptive

  • put a copy of your yaml definition in the copy.

  • open the swagger-initializer.js file and change the url of the definition file to the file in the local folder.

  • now serve the folder.