EDDYMENS

Published 2 years ago

OpenAPI Specifications: How To Make Them

Table of contents


What is the OpenAPI Specification?

The OpenAPI Specification is a specification that describes a well-laid-out approach to defining the structure of a RESTful API. There are many ways you can describe the APIs you create. The OpenAPI specification makes it easy by defining a set of rules and providing guidelines that anyone can follow to document their APIs which can later be used to understand how an API works. This presents a whole host of opportunities especially the fact that the same specification can be fed to computers to generate many other things including documentation and even code snippets.


What is it good for?

The OpenAPI specification looks like a config file, in fact, you can draft them using either the JSON or YAML format which themselves dominate the config file space.

Because they can be interpreted by computers you can do a whole host of things, from generating code in any language that brings the API to live to generating SDK-like code samples that other developers can use to connect to your API. You can even generate interactive documentation as well.

Swagger [↗] the project under which the specification standard was created also provides tools to generate some of the things I mentioned earlier. In the next section, we are going to look at how to use one of such tools to create a sample OpenAPI specification.


How do you create one?

swaggerEdtior [↗]

Knowing YAML [↗] and having a code/text editor is half what you need to create an OpenAPI spec. One of the tools the swagger team provides is a browser-based editor called the Swagger Editor [↗] designed specifically for creating and editing the OpenAPI spec. It comes with an inbuilt interactive previewer, syntax highlighting, and error detection among other things.

For our tutorial, find the complete specification here [↗], copy-paste this into the Swagger editor to follow along with the tutorial. It's for an API for storing and managing books and their page sizes.

OpenAPI Specification structure

You can write your spec using either JSON or YAML as I mentioned before, However, most specs are written in YAML because its a lot more readable compared to JSON. YAML's syntax is based on key/value pairs and uses indentation to denote information hierarchy as we will see in a bit.

The OpenAPI spec itself is broken up into many parts with each section representing a key part of our spec definition:

  • MetaData: This section holds information describing what our API does as well as information regarding the version of the OpenAPI standard we are using.
  • Server: This section holds information regarding the server that hosts our API, for example, the URL to access it.
  • Paths: This is the meat of our spec, it defines every part and option for each API a user can access.
  • Components: This section is reserved to hold reusable parts of our spec, for example, if two endpoints have the same input, we don't have to define it twice we can store it in the component section and use just reference it in both places.
  • Security: Every security element of our API from API keys to 0Auth is defined under the security section.

The above sections are by no means an exhaustive list, they however are the core sections needed to have a comprehensive spec.

Adding our metadata and server structures

01: openapi: 3.0.0 02: info: 03: description: The books endpoint keeps a record of books and their page numbers 04: version: 1.0.0 05: title: Book API 06: servers: 07: - url: https://lit-taiga-02928.herokuapp.com 08: - url: http://lit-taiga-02928.herokuapp.com

Now let's start by breaking down our pre-created spec [↗]The first line specifies the version of the OpenAPI we are working with. In this case version 3, followed by a section describing the API we are designing the spec for.

The second section servers: holds information regarding the server hosting our API.

Also, note how we indent some of the key/value pairs in each section. Parts of the YAML file are indented when they form part of a section and the end of the indentation marks the beginning of a new section.

So for our example above this will mean description, version, and title are part of the info: section, and servers: is a different section with url being a part of that.

The next step is to define our endpoints, and this is done under the path: key, you may have also noticed the swagger editor hinting at this in the form of an error.

Defining our endpoint and method structure

01: paths: 02: /: 03: get: 04: summary: Get the list of all books 05: description: This endpoint will return the list of all books 06: # TODO: Add response structure 07: post: 08: summary: Add a new book 09: description: Add a new book to the book archives 10: # TODO: Add request and response structure 11: "/{id}": 12: put: 13: summary: Update an existing book 14: description: Update an existing book in the books archives 15: # TODO: Add request and response structure 16: delete: 17: summary: Deletes a book 18: description: Delete a book from the list 19: # TODO Add request and response structure

To denote the beginning of the endpoint definition section we add the paths: key, right after which we list all the sub URLs or endpoints in our case thus / and /{id}. The indentation of get and post under / implies that both request types use the same URL structure, likewise put and delete have the URL structure /{id} where id is a placeholder for the id of the book the user will like to update or delete respectively.

With our URL definitions and descriptions out of the way, the next step is to define the request structure for our APIs.

Defining the request structure

01: paths: 02: /: 03: get: 04: # ... 05: put: 06: summary: ... 07: parameters: 08: - name: id 09: in: path 10: description: ID of the book you will like to update 11: required: true 12: schema: 13: type: integer 14: format: int64 15: requestBody: 16: content: 17: application/json: 18: schema: 19: $ref: "#/components/schemas/Book" 20: description: JSON payload of the book that needs to be updated 21: required: true 22: # TODO: response structure 23: 24: components: 25: schemas: 26: Book: 27: type: object 28: required: 29: - title 30: - pages 31: properties: 32: title: 33: type: string 34: example: Harry Potter 35: pages: 36: type: integer 37: example: 123

Besides the GET request all the other three requests will require some form of input or request data. For example in other to add a new book you will need to provide the new book as a request JSON body, same thing applies to the put request this will be set under the requestBody: key.

Also in the case of put and delete, the book you will like to either update or delete requires you to pass in the ID as a path parameter, which is defined under the parameter: section.

Note the line $ref: "#/components/schemas/Book" in the requestBody: this points to the components section which holds reusable parts of our specification. In this case, the book JSON structure is mocked and reused in different parts of our spec. Later we will see this also being used in our response sections.


Defining the response structure

Next let's write out the spec for our get response, this is meant to return the list of all our books in JSON format as shown below.

01: { 02: "message":"List of books", 03: "payload":[ 04: { 05: "title":" In Search of Lost Time ", 06: "pages":4215 07: }, 08: { 09: "title":" Ulysses", 10: "pages":730 11: }, 12: { 13: "title":"The Great Gatsby", 14: "pages":208 15: }, 16: { 17: "title":"Moby Dick", 18: "pages":378 19: } 20: ] 21: }

Let's take the above JSON response and represent it in our spec as seen below in the response: section. You will also notice there are two sections 200 and 405, these are HTTP status codes [↗], and anything under them describes what the user gets back depending on the response from the server.

The important section here is schema: which holds a YAML version of our JSON response above. This takes a bit of practice to learn to convert JSON to YAML and I will recommend taking some time to read on OpenAPI Data Types [↗] as this helps

01: responses: 02: "200": 03: description: successful operation 04: content: 05: application/json: 06: schema: 07: type: object 08: properties: 09: message: 10: type: string 11: example: Book added successfully 12: payload: 13: type: array 14: items: 15: $ref: "#/components/schemas/Book" 16: "405": 17: description: Invalid input

Testing and debugging

Swagger UI walk through [↗]

During the creation of your spec using the Swagger Editor [↗] you get a lot of useful things such as the Editor letting you know if there is something wrong with your spec and on which line and sometimes a possible fix.

You are also able to test out your APIs to see if you captured them correctly in your spec. To do this you just need to expand the right endpoint in the interactive section to the right and click on the "Try it out" button, this will provide you with input boxes to fill out the needed request data and then generate a CURL [→] based code snippet. You can then run this in your command line to see the results. If you get the right result then you know your spec is correct

Conclusion

For each part of our specification, there are many more options we could have added, we didn't use most of the constructs because I wanted to keep the tutorial simple. Depending on the structure of your API you might need to use some of these constructs, for example, auth. To know which constructs are available to you, you should refer to the OpenAPI documentation [↗].

Also once you have an OpenAPI spec you can convert this into documentation for your end users using a tool like Redocly [↗], I have another article [↗] covering this if you are interested.

Here is another article you might like 😊 Simple OpenAPI starter template