Intro to Swagger – A Structured Approach to Creating an API

Creating an API can be difficult when starting from scratch. And working with a team on determining the structure and testing an API can further complicate the process. Swagger can help this process move more smoothly.

In case you’re not familiar with it, Swagger is a way to standardize and design an API that uses the OpenAPI Specification (OAS). It is a widely used framework for simplifying the process of creating an API with either YAML or JSON. There’s even a site to help you write your Swagger files, which I highly recommend thanks to its fairly responsive error checking and many other features including converting between JSON and YAML.

A Quick Word About Versions 2.0 and 3.0

Version 3.0.0 has several updates from Version 2.0, but it has primarily been changed to use OpenAPI 3. If you compare the two, you’ll notice that the initial defining of URLs is slightly different, there’s more support of JSON Schema, the markdown has changed from GitHub Flavored Markdown to CommonMarkdown, and Semver is used for versioning. Check out Swagger’s blog for more on what’s been updated in Version 3.

I would highly recommend using Version 3 when possible, as it’s the most up-to-date and has some interesting new features. My example will focus on Version 2, however, due to the fact that AWS API Gateway does not yet allow for Version 3. This is one of the large drawbacks of Swagger V.3 (for now).

Here’s a simple example of a Swagger file using Version 3.

What Does a Swagger File Look Like?

I started by creating a super-simple API for a library. Each Swagger file must begin with:

swagger: "2.0"

The second section specifies some metadata for the API:


info:
  version: 2018.7.28
  title: Library API
  description: A simple API for a library
  contact:
    name: Gillian Lemke
    email: [email protected]
    url: http://testwebsite.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
host: api.testwebsite.com
basePath: /api
schemes:
  - http
consumes:
  - application/json
produces:
  - application/json

Now for the fun part: actually defining the API routes. The path section is where the requests are defined for each route, with additional configuration for different status codes, types, and response data.


paths:
  /books:
    get:
      description: Returns all books in the library
      operationId: findBooks
      responses:
        "200":
          description: book response
          schema:
            type: array
            items:
              $ref: '#/definitions/Book'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'
    post:
      description: Creates a new book in the library. Duplicates are allowed
      operationId: addBook
      parameters:
        - name: book
          in: body
          description: Book to add to the library
          required: true
          schema:
            $ref: '#/definitions/NewBook'
      responses:
        "200":
          description: book response
          schema:
            $ref: '#/definitions/Book'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'
  /books/{id}:
    get:
      description: Returns a book based on id
      operationId: find book by id
      parameters:
        - name: id
          in: path
          description: id of book to fetch
          required: true
          type: integer
          format: int64
      responses:
        "200":
          description: book response
          schema:
            $ref: '#/definitions/Book'
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'
    delete:
      description: deletes a single book based on the id
      operationId: deleteBook
      parameters:
        - name: id
          in: path
          description: id of book to delete
          required: true
          type: integer
          format: int64
      responses:
        "204":
          description: book deleted
        default:
          description: unexpected error
          schema:
            $ref: '#/definitions/Error'

As you’ve probably noticed, I did not specifically define the response schema for each status code. Instead, you can use $ref, which points to the definitions section.


definitions:
  Book:
    allOf:
      - $ref: '#/definitions/NewBook'
      - required:
        - id
        properties:
          id:
            type: integer
            format: int64
  NewBook:
    required:
      - name  
    properties:
      name:
        type: string
      tag:
        type: string    
  Error:
    required:
      - code
      - message
    properties:
      code:
        type: integer
        format: int32
      message:
        type: string

While implementing this API with AWS API Gateway, specificity is incredibly important–so much so that defaults are not allowed.

How Can a Swagger File Be Used?

As mentioned, I have been researching using AWS API Gateway. There is a feature to import a Swagger file which directly generates your API for you. Similar services such as Google Cloud Platform have support for Swagger implementation, as well.

One common use for Swagger is documentation. It provides a clear, easy-to-read source of what the API looks like. A documentation tool called ReDoc can use Swagger files to generate a more user-friendly interface for API documentation and can easily be used with Yarn/npm.

Swagger can also be used for testing an API with a tool called Swagger Inspector, which has a really helpful tutorial.

I hope you’ve found this introduction useful. Even if you aren’t using Swagger to test or implement your API, it is at least helpful to define your API and its attributes for both internal and external conversation.