OpenAPI 3.0 Tutorial

OpenAPI 3.0 is an open-source format for describing and documenting APIs. In this tutorial, we will write a simple API definition in the OpenAPI 3.0 format. If you are using OpenAPI 2.0 (Swagger 2.0), see this tutorial instead.

What Is OpenAPI?

OpenAPI Specification (formerly known as Swagger Specification) is an open-source format for describing and documenting APIs. The Specification was originally developed in 2010 by Reverb Technologies (formerly Wordnik) to keep the API design and documentation in sync. It has since become a de-facto standard for designing and describing RESTful APIs and is used by millions of developers and organizations for developing their APIs, be it internal or client-facing.

The latest version of OpenAPI is 3.0. OpenAPI definitions can be written in JSON or YAML. We recommend YAML because it is easier to read and write.

A simple OpenAPI 3.0 specification looks like this:

openapi: 3.0.0
info:
  version: 1.0.0
  title: Sample API
  description: A sample API to illustrate OpenAPI concepts
paths:
  /list:
    get:
      description: Returns a list of stuff              
      responses:
        '200':
          description: Successful response

The syntax above will make sense once we finish this walkthrough.

About this tutorial

In this tutorial, we will guide you through building a simple API while covering all the important aspects of the OpenAPI Specification.

Prerequisites

  • Basic knowledge of RESTful APIs.

  • Basic knowledge of YAML is recommended.

We recommend using SwaggerHub’s built-in editor and validator to quickly write and visualize the OpenAPI definition described in this tutorial. SwaggerHub is free with loads of features to get you started quickly, so give it a try!

Create an API

We will be designing an API for a record label. Let’s assume that the record label has a database of artists with the following information:

  • Artist name

  • Artist genre

  • Number of albums published under the label

  • Artist username

The API will let consumers obtain the list of artists stored in the database and add a new artist to the database.

An API defined using the OpenAPI Specification can be divided into 3 main sections –

  • Meta information

  • Path items (endpoints):

    • Parameters

    • Request bodies

    • Responses

  • Reusable components:

    • Schemas (data models)

    • Parameters

    • Responses

    • Other components

Meta information

Let’s start with a simple API definition that contains just meta information, such as the API title, version, server URL and other descriptive information.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple Artist API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

# Basic authentication
components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

paths: {}

Each API definition starts with the version of the OpenAPI Specification that this definition uses. In our example, it is openapi: 3.0.0. The info object contains the API title and version, which are required, and an optional description.

The servers array specifies one or more server URLs for API calls. The API endpoint paths are appended to the server URL. Some APIs have a single server, and others may have multiple servers, such as production and sandbox. In our example, the server URL is https://example.io/v1.

We also secure the API using Basic authentication, so that only authorized users can consume the API. For more advanced security, see Authentication.

Path items

The path items are the endpoints of your API under which you can specify HTTP verbs for manipulating the resources in the desired manner. These endpoints are relative to the server URL, which in our example is https://example.io/v1.

We will define the /artists endpoint and the GET method for this endpoint. So, a client will use GET https://example.io/v1/artists to get a list of artists.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

#  ----- Added lines  ----------------------------------------
paths:
  /artists:
    get:
     description: Returns a list of artists 
#  ---- /Added lines  ----------------------------------------

Responses

The GET method, under the artists endpoint, lets the API consumer obtain the details of a list of artists from the https://example.io/v1 database.

Every response would need at least one HTTP status code to describe the kind of responses a consumer is likely to expect. The description gives details on what the responses of the API would be. In our sample code, we have specified 200, which is a successful client request, while 400 is an unsuccessful request. You can find more information about HTTP status codes here. A successful response will return the artist name, genre, username, and albums recorded. An unsuccessful request is described under the 400 HTTP code, with a corresponding error message detailing why the response is invalid.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      #  ----- Added lines  ----------------------------------------
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - username
                  properties:
                    artist_name:
                      type: string
                    artist_genre:
                      type: string
                    albums_recorded:
                      type: integer
                    username:
                      type: string

        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string
      #  ---- /Added lines  ----------------------------------------

See Describing Responses for more information on responses.

Parameters

RESTful parameters specify the variable part of the resource a user works with. If you want advanced information on parameters, see Describing Parameters.

Query parameters

Query parameters are the most common type of parameters. They appear at the end of a URL following a question mark. Query parameters are optional and nonunique so that they can be specified multiple times in the URL. It is a non-hierarchical component of the URL.

In our example, let the client limit the information required instead of returning the entire list of artists from the database, which would lead to an unnecessary load on the server. The client could describe the page number (offset) and the amount of information on the page (limit), for example:

GET https://example.io/v1/artists?limit=20&offset=3

These variables are defined under the parameters object in the OpenAPI definition. Thus, the specification would now look as follows –

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      #  ----- Added lines  ----------------------------------------
      parameters:
        - name: limit
          in: query
          description: Limits the number of items on a page
          schema:
            type: integer
        - name: offset
          in: query
          description: Specifies the page number of the artists to be displayed
          schema:
            type: integer
      #  ---- /Added lines  ----------------------------------------         
      
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - username
                  properties:
                    artist_name:
                      type: string
                    artist_genre:
                      type: string
                    albums_recorded:
                      type: integer
                    username:
                      type: string

        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

Request body

POST, PUT, and PATCH requests typically contain the request body. The request body is defined by using the requestBody object. For this API, let’s add the ability for a user to post an artist to our database. This would be under the /artists resource.

The API would now look as follows:

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      parameters:
        - name: limit
          in: query
          description: Limits the number of items on a page
          schema:
            type: integer
        - name: offset
          in: query
          description: Specifies the page number of the artists to be displayed
          schema:
            type: integer
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - username
                  properties:
                    artist_name:
                      type: string
                    artist_genre:
                      type: string
                    albums_recorded:
                      type: integer
                    username:
                      type: string
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

    #  ----- Added lines  ----------------------------------------
    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object 
              required:
                - username
              properties:
                artist_name:
                  type: string
                artist_genre:
                  type: string
                albums_recorded:
                  type: integer
                username:
                  type: string

      responses:
        '200':
          description: Successfully created a new artist

        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string
    #  ---- /Added lines  ----------------------------------------

Path parameters

The path parameters can be used to isolate a specific component of the data that the client is working with, for example, https://example.io/v1/artists/{username}. Path parameters are part of the hierarchical component of the URL and are thus stacked sequentially.

Let’s create a new endpoint that returns a specific artist’s information based on the username provided. The path parameter here would be the username of the artist whose info we need. Path parameters (username in this case) have to be mandatorily described in the parameters object under the method.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      parameters:
        - name: limit
          in: query
          description: Limits the number of items on a page
          schema:
            type: integer
        - name: offset
          in: query
          description: Specifies the page number of the artists to be displayed
          schema:
            type: integer
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - username
                  properties:
                    artist_name:
                      type: string
                    artist_genre:
                      type: string
                    albums_recorded:
                      type: integer
                    username:
                      type: string
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object 
              required:
                - username
              properties:
                artist_name:
                  type: string
                artist_genre:
                  type: string
                albums_recorded:
                  type: integer
                username:
                  type: string
      responses:
        '200':
          description: Successfully created a new artist
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

  #  ----- Added lines  ----------------------------------------
  /artists/{username}:
    get:
      description: Obtain information about an artist from his or her unique username
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          
      responses:
        '200':
          description: Successfully returned an artist
          content:
            application/json:
              schema:
                type: object
                properties:
                  artist_name:
                    type: string
                  artist_genre:
                    type: string
                  albums_recorded:
                    type: integer
                
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object 
                properties:           
                  message:
                    type: string
  #  ---- /Added lines  ----------------------------------------

Here, we have specified the username as a path parameter. The thing to note is that path parameters have to have a true property set to the required parameter, for the spec to be valid.

If you followed through till here, then congratulation! You have just designed a simple API for a record label!

Reusable components

What we have just described are just 2 endpoints and 3 actions. This was about 130 lines of specification, and the spec will only get longer as the API gets bigger. One of the things you may notice in the spec we have so far is that we have the same Artist schema (artist name, genre, username and albums published) that gets repeated in various 200 and 400 responses. Bigger APIs would involve rewriting and reusing a lot of the same specs, so it would be a tedious task writing a more complex API.

The OpenAPI Specification has a solution – reusable components that can be used across multiple endpoints in the same API. These components are defined in the global components section and then are referenced in individual endpoints. The Specification defines various types of reusable components:

  • Schemas (data models)

  • Parameters

  • Request bodies

  • Responses

  • Response headers

  • Examples

  • Links

  • Callbacks

Schemas

The schemas subsection of the global components section can contain various data models consumed and returned by the API. Here is how we can use components to store the schema for an HTTP 200 OK response. Our API definition already had the components section containing securitySchemes, now we have moved components to the bottom and added the schemas subsection.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      parameters:
        - name: limit
          in: query
          description: Limits the number of items on a page
          schema:
            type: integer
        - name: offset
          in: query
          description: Specifies the page number of the artists to be displayed
          schema:
            type: integer
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  #  ----- Added line  ----------------------------------------
                  $ref: '#/components/schemas/Artist'
                  #  ---- /Added line  ----------------------------------------
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              #  ----- Added line  ----------------------------------------
              $ref: '#/components/schemas/Artist'
              #  ---- /Added line  ----------------------------------------
      responses:
        '200':
          description: Successfully created a new artist
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

  /artists/{username}:
    get:
      description: Obtain information about an artist from his or her unique username
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          
      responses:
        '200':
          description: Successfully returned an artist
          content:
            application/json:
              schema:
                type: object
                properties:
                  artist_name:
                    type: string
                  artist_genre:
                    type: string
                  albums_recorded:
                    type: integer
                
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object 
                properties:           
                  message:
                    type: string

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic

  #  ----- Added lines  ----------------------------------------                
  schemas:
    Artist:
      type: object
      required:
        - username
      properties:
        artist_name:
          type: string
        artist_genre:
            type: string
        albums_recorded:
            type: integer
        username:
            type: string
  #  ---- /Added lines  ----------------------------------------

The spec is shorter, and whenever a new endpoint with the same schema is needed, the designer does not need to spend time writing the piece. See here for more information about components.

Parameters and Responses

The components section also has subsections for storing reusable parameters and responses. In the spec below, we define the reusable query parameters offset and limit and then reference them in the /artists endpoint. We also define a reusable 400Error response, which we then reference from all the endpoints.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      parameters:
        #  ----- Added line  ------------------------------------------
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        #  ---- /Added line  ------------------------------------------
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  #  ----- Added line  --------------------------------
                  $ref: '#/components/schemas/Artist'
                  #  ---- /Added line  --------------------------------
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------

    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              #  ----- Added line  ------------------------------------
              $ref: '#/components/schemas/Artist'
              #  ---- /Added line  ------------------------------------
      responses:
        '200':
          description: Successfully created a new artist
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------    

  /artists/{username}:
    get:
      description: Obtain information about an artist from his or her unique username
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          
      responses:
        '200':
          description: Successfully returned an artist
          content:
            application/json:
              schema:
                type: object
                properties:
                  artist_name:
                    type: string
                  artist_genre:
                    type: string
                  albums_recorded:
                    type: integer
                
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------     

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic

  schemas:
    Artist:
      type: object
      required:
        - username
      properties:
        artist_name:
          type: string
        artist_genre:
            type: string
        albums_recorded:
            type: integer
        username:
            type: string

  #  ----- Added lines  ----------------------------------------
  parameters:
    PageLimit:
      name: limit
      in: query
      description: Limits the number of items on a page
      schema:
        type: integer
      
    PageOffset:
      name: offset
      in: query
      description: Specifies the page number of the artists to be displayed
      schema:
        type: integer

  responses:
    400Error:
      description: Invalid request
      content:
        application/json:
          schema:
            type: object 
            properties:
              message:
                type: string
  #  ---- /Added lines  ----------------------------------------

To jump to a definition, simply click the $ref link.

Summary

We have successfully designed a RESTful API that exposes the artists in the record label database. We have only covered the basics of OpenAPI, as the specification can be anything you want it to be (mostly). If you want to become an OpenAPI expert, you can refer to the full OpenAPI 3.0 specification and to the how-to guide, or try out our certification courses!

See Also

Publication date: