Writing OpenAPI (Swagger) Specification Tutorial Series - Part 7

Documentation

By Arnaud Lauret, June 12, 2016

Previous posts showed how to write a highly accurate description of an API interface contract with the OpenAPI specification. But an interface contract, no matter how brilliant, is nothing without some explainations. A fully documented OpenAPI specification file can provide some useful information and be used as a part of an API’s documentation.

Writing OpenAPI (Swagger) Specification Tutorial Series

This tutorial teaches everything about the OpenAPI 2.0 Specification (fka. as Swagger), most of what you’ll read here can still be applied on version 3.

If you’re a bit lost in the specification (version 2 or 3), take a look at the OpenAPI Map:

In previous parts we’ve learned to create highly accurate API description, in this seventh part we’ll learn how to use the OpenAPI specification to make it a valuable part of an API documentation.

API’s general informations

First things first. When using an API, API consumers want to have some general informations about it like its version, its name, some description, term of service, how to contact the API provider, what kind of licencing it uses. The info object placed on root level can be used to provide such information:

info:
  version: 1.1.0
  title: Simple API
  description: A simple API to learn how to write OpenAPI Specification
  termsOfService: http://simple.api/terms-of-service
  contact:
    name: John Doe
    url: http://simple.api/contact
    email: [email protected]
  license:
    name: Apache-2.0
    url: http://www.apache.org/licenses/LICENSE-2.0

Categorizing operations with Tags

By using tags on operation level, we can categorize operations. tags is a simple list of names.

Single tag

This operation belongs to the Person category:

paths:
  /persons:
    parameters:
      - $ref: '#/parameters/userAgent'
    get:
      summary: Gets some persons
      description: Returns a list containing all persons. The list supports paging.
      operationId: searchUsers
      tags:
        - Persons

Multiple tags

An operation can belong to different categories:

  /js-less-consumer-persons:
    parameters:
      - $ref: '#/parameters/userAgent'
    post:
      summary: Creates a person
      description: For JS-less partners
      operationId: createUserJS
      deprecated: true
      tags:
        - JSLess
        - Persons

Descriptions everywhere

We can add some descriptions at almost every level of the OpenAPI specification.

Security definitions

A description can be added to security definitions:

securityDefinitions:
  OauthSecurity:
    description: New Oauth security system. Do not use MediaSecurity or LegacySecurity.
    type: oauth2
    flow: accessCode
    authorizationUrl: 'https://oauth.simple.api/authorization'
    tokenUrl: 'https://oauth.simple.api/token'
    scopes:
      admin: Admin scope
      user: User scope
  MediaSecurity:
    description: Specific media security for backward compatibility. Use OauthSecurity instead.
    type: apiKey
    in: query
    name: media-api-key
  LegacySecurity:
    description: Legacy security system for backward compatibility. Use OauthSecurity instead.
    type: basic

Schema title and description

Each schema (used in a definition, a parameter or a response) can have a title and a description:

definitions:
  Person:
    title: Human
    description: A person which can be the user itself or one of his friend

  SpokenLanguages:
    title: Languages
    description: A hashmap of spoken languages

Property description

Properties can be described with description:

    properties:
      firstName:
        description: first name
        type: string

Parameter’s description

Whether defined inline or in parameters section, a parameter can have a description.

Inline parameter’s description

paths:
  /persons:

    post:

      parameters:
        - name: person
          in: body
          required: true
          description: The person to create.
          schema:
            $ref: '#/definitions/Person'

Reusable parameter’s description

parameters:
  username:
    name: username
    in: path
    required: true
    description: The person's username
    type: string
  pageSize:
    name: pageSize
    in: query
    description: Number of persons returned

Operation’s summary, description and operationId:

An operation can be described with a summary and a longer description. An operationId can be added. It can be used as a link to the implementation running behind the API for example.

paths:
  /persons:
    parameters:
      - $ref: '#/parameters/userAgent'
    get:
      summary: Gets some persons
      description: Returns a list containing all persons. The list supports paging.
      operationId: searchUsers

Response’s description

Whether inline or defined in responses, a response can have a description.

Inline response’s description

paths:
  /persons:
    parameters:
      - $ref: '#/parameters/userAgent'
    get:

      responses:
        '200':
          description: A list of Person

Reusable response’s description

responses:
  Standard500ErrorResponse:
    description: An unexpected error occured.

Reponse’s header’s description

Headers returned with a response can have a description:

    headers:
      X-Rate-Limit-Remaining:
        description: How many calls consumer can do
        type: integer
      X-Rate-Limit-Reset:
        description: When rate limit will be reset
        type: string
        format: date-time

Tags descriptions

Tags can have descriptions. We need to add a tags section on specification file root level, on each item in this list we set a name (corresponding to the name used in tags list on operation level) and a description:

tags:
  - name: Persons
    description: Everything you need to handle users and friends

Using GFM in descriptions

In almost all description, we can use GFM (Github Flavored Markdown). To check if an object description support GFM, take a look at my visual documentation or the original specification. Note that GFM support can vary depending the tool processing the OpenAPI specification file.

Simple multiline description

By adding a | and a new line with a new tab, we can write multiline descriptions:

  '/persons/{username}/collecting-items':
    parameters:
      - $ref: '#/parameters/username'
      - $ref: '#/parameters/userAgent'
    get:
      summary: Gets a person's collecting items list
      description: |
        Returns a list containing all items this person is looking for.  
        The list supports paging.

Simple GFM description

externalDocs: 
  description: |
    **Complete** documentation describing how to use this API
  url: http://doc.simple.api/

Description with array

  CollectingItem:
    discriminator: itemType
    required:
      - itemType
    properties:
      itemType:
        description: |
          An item can be of different type:
            
          type | definition
          -----|-----------
          Vinyl| #/definitions/Vinyl
          VHS  | #/definitions/VHS
          AudioCassette | #/definitions/AudioCassette
        type: string
        enum:
          - AudioCassette
          - Vinyl
          - VHS

Description with code

swagger: '2.0'

info:
  version: 1.1.0
  title: Simple API
  description: |
    A simple API to learn how to write OpenAPI Specification.
    This file uses almost every single aspect of the [Open API Specification](https://openapis.org/).  
    This API will use JSON.  
    JSON looks like this:  
    
    ```JSON
    {
      "key": "value",
      "anotherKey": "anotherValue"
    }
    ```
  termsOfService: http://simple.api/terms-of-service

Examples

Examples can be provided for atomic or object properties, definitions, and responses.

Atomic property example

Atomic properties can be illustrated with an example:

    properties:
      firstName:
        description: first name
        type: string
        example: John
      lastName:
        description: last name
        type: string
        example: Doe
      username:
        description: Username used to connect to the service
        type: string
        pattern: '[a-z0-9]{8,64}'
        minLength: 8
        maxLength: 64
        example: john1doe6
      dateOfBirth:
        description: Date of birth
        type: string
        format: date
        example: 1978-06-21
      lastTimeOnline:
        description: The last time this person was connected to the service as a 
        type: string
        format: date-time
        readOnly: true
        example: 2016-06-10T12:36:58.014Z

Object property example

Object properties, can also be illustrated with a complex example complying to the underlying JSON Schema:

  Persons:
    title: Humans
    description: A list of users or friends
    required:
      - items
    properties:
      items:
        description: Array containg the list
        type: array
        minItems: 10
        maxItems: 100
        uniqueItems: true
        items:
          $ref: '#/definitions/Person'
        example:
          - firstname: Robert
            lastname": Doe
            username": robdo
            dateOfBirth: 1970-01-28
            lastTimeOnline: 2016-04-10T14:36:58.014Z
          - firstname: Jane
            lastname: Doe
            username: jdoe123
            dateOfBirth: 1980-05-12
            lastTimeOnline: 2016-05-12T19:23:59.014Z

Definition Example

An example can be defined for the entire definition just like for an object property (it must conforms to its underlying JSON schema):

  MultilingualErrorMessage:
    title: MultiLingualMultiDeviceErrorMessage
    description: An multilingual error message (hashmap) with a long and a short description
    additionalProperties:
      $ref: '#/definitions/ErrorMessage'
    properties:
      defaultLanguage:
        $ref: '#/definitions/ErrorMessage'
    example:
      defaultLanguage:
        longMessage: We're deeply sorry but an error occured
        shortMessage: Error
      fr:
        longMessage: Nous sommes désolé mais une erreur est survenu
        shortMessage: Erreur

Response’s Example

On response level, we can provide example, each one corresponding to a media type returned by the operation. Here’s an example for an application/json media type:

  '/persons/{username}/collecting-items':

    get:

      responses:
        '200':

          examples:
            application/json:
              {
                "totalItems": 10,
                "totalPage": 4,
                "pageSize": 3,
                "currentPage": 2,
                "items": 
                [
                  { 
                    "itemType": "Vinyl",
                    "maxPrice": 20,
                    "imageId": "98096838-04eb-4bac-b32e-cd5b7196de71",
                    "albumName": "Captain Future Original Soundtrack",
                    "artist": "Yuji Ohno"
                  },
                  { 
                    "itemType": "VHS",
                    "maxPrice": 10,
                    "imageId": "b74469bc-e6a1-4a90-858a-88ef94079356",
                    "movieTitle": "Star Crash",
                    "director": "Luigi Cozzi"
                  },
                  { 
                    "itemType": "AudioCassette",
                    "maxPrice": 10,
                    "imageId": "b74469bc-e6a1-4a90-858a-88ef94079356",
                    "albumName": "Star Wars",
                    "artist": "John Williams"
                  }
                ]
              }
        '404':
          $ref: '#/responses/PersonDoesNotExistResponse'
        '500':
          $ref: '#/responses/Standard500ErrorResponse'
        default:
          $ref: '#/responses/TotallyUnexpectedResponse'

Examples precedence

If we defined examples on multiple levels (property, object, definition, response), it’s always the higher level which is taken into account by tools processing OpenAPI specification file.

Operation’s deprecation

An operation can be deprecated by setting deprecated to true:

  /js-less-consumer-persons:
    parameters:
      - $ref: '#/parameters/userAgent'
    post:
      summary: Creates a person
      description: For JS-less partners
      operationId: createUserJS
      deprecated: true

Links to external API documentation

In most case, the OpenAPI specification file MUST NOT be the only API’s documentation. How to create an application key, use cases, operation chaining and many other things need to be documented. All these other documentations can be separated from the specification file. However, the OpenAPI specification allow to provide links to these other documentations when needed.

We can add an externalDoc object on root level with a link to the API documentation.

externalDocs: 
  description: Complete documentation describing how to use this API
  url: http://doc.simple.api/

Each operation can have its own link to an external documentation using the same externalDoc object:

  /images:
    parameters:
      - $ref: '#/parameters/userAgent'
    post:
      summary: Uploads an image
      description: Upload an image, will return an image id. 
      operationId: storeImage
      externalDocs:
        description: How to upload media
        url: http://doc.simple.api/media/upload

A tag’s description can also provide an external link:

tags:
  - name: Persons
    description: Everything you need to handle users and friends
    externalDocs:
      description: Peopls category documentation
      url: http://doc.simple.api/people

Conclusion

Even if an OpenAPI specification MUST NOT be the only documentation for an API, it can be a good part of it, and you now have mastered this aspect of the specification. In next post we’ll see how we can split a OpenAPI specification file in different files.

By continuing to use this web site you agree with the API Handyman website privacy policy (effective date , June 28, 2020).