Table of contents
- Introduction
- What is an API?
- What are endpoints?
- What is an HTTP method?
- HTTP request body
- HTTP response body
- HTTP status code
- Let's make an API call
- Better tools for making API calls
- APIs as a product
- Documenting APIs
- What is OpenAPI?
- Introduction to YAML
- Documenting APIs with OpenAPI
- Hosting OpenAPI documentation
- Conclusion
Introduction
In this tutorial, we will look at how to document HTTP-based APIs [→] using the OpenAPI Specification [↗].
My suggestion will be to spread out this tutorial by reading a section a day. I believe this will help prevent information overload.
What is an API?
Let's start by looking at APIs, feel free to skip this section if you are already familiar with them.
Imagine yourself on an e-commerce website, browsing through the latest sneakers. You click through a few of them, add one or two to the cart, head to check-out, provide your delivery details, and finally make payments to complete the transaction.
Under the hood several things could be happening, for example, the website might call on a third-party payment processor to process your payments.
Since the payment processor is a separate service from the e-commerce website, they both need to figure out a way to communicate with each other. APIs are mostly used to facilitate these types of communications.
APIs are nothing more than URLs, like the one you type into your browser but this time round, instead of returning a user interface, it returns some form of structured data that the requesting system will understand. For example, JSON [↗].
What are endpoints?
A collection of endpoints makes up an API. Continuing from the analogy we started above, while on the e-commerce site, you perform different actions. You request a list of shoes, you submit your purchase request, and maybe you update your cart content or even delete items from your cart.
When it comes to APIs these actions can be designated to different URLs called endpoints. So when those specific URLs are called the system understands the action the user wants to perform.
You can read more about endpoints from the "What is An API Endpoint?" [↗] guide.
Note: The next few sections will sound very abstract and hard to conceptualize, don't give up! Once we begin looking at examples everything will begin to make sense.
What is an HTTP method?
The Hypertext Transfer Protocol (HTTP) [↗] is the standard protocol that is followed when it comes to moving data over the internet.
This standard defines the different parts of Internet communication such as how to craft a request to send out and what to expect in terms of response.
HTTP request methods are a part of the standard that defines the type of request you would like to perform.
We will try and conceptualize this in later sections but for now, it helps to think of HTTP as a spoken language and HTTP request methods as the verbs used in this language. There are a number of them but we will look at the common four for now, namely GET, POST, PATCH, DELETE.
So a sample request for all four verbs will sound something like this:
- GET: "I would like to request the list of shoes".
- POST: "I would like to buy the Nike Airforce in black"
- PATCH: "I would like to update my purchase to a Nike Airforce in white"
- DELETE: "I would like to cancel my purchase"
HTTP request body
Now that we have looked at some methods used to make different types of requests, let's look at request bodies. A request body is data that is attached to a request. For example, you need to add information about the shoe you would like to purchase when you make the POST request.
This data can be sent as JSON [↗].
Sometimes you need to pass in data when making a GET request. For example, if you use a GET request to fetch the list of available shoes you might want to get only ones in black. You can pass this information as a query parameter [↗].
HTTP response body
When we send out a request we expect a response, what we get back is known as the "Response Body". This data can also be sent back to us as JSON [↗] or HTML [↗] which can then be rendered/visualized in the browser for the end user to see.
HTTP status code
A specific code is sent back to you as part of the Response, this is known as the HTTP status code. This gives you a general idea regarding the state of your request.
Here are some examples:
- 200 OK: This response status code signifies the requested action was performed successfully.
- 201 Created: This status code is sent whenever a POST request goes through successfully.
- 202 Accepted: This is sent back to let the requester know the request has been accepted and is being processed.
- 204 No Content: This is sent back to signify the action has been performed but there is no output. For example, when making a DELETE request.
- 301 Moved Permanently: Sometimes the URL you send your request to has been replaced. In this case, the receiving system will reroute your request to the new URL but will respond with this status code to let you know about the change.
- 404 Not Found: In situations where the request URL does not exist the system will respond with a 404 status code.
- 403 Forbidden: This is sent back when the URL exists but you are not allowed to access it.
- 500 Internal Server Error: 500 error means your request is ok but the system is unable to process it due to an internal issue.
Let's make an API call
Let me say this again, APIs are nothing more than implementations that make it possible for computers within a network like the internet to interact with each other. Where we humans click around and type in data, APIs provide a standardized approach to doing the same thing programmatically.
Whenever you click and type out data, HTTP requests are made underhood to move your instructions around.
Now let's try and simulate a sample request, shall we?
In this example, we are going to simulate adding a new user Morpheus
to a system using an API.
- Head over to https://reqbin.com [↗]
- In the URL bar copy and paste the following URL
https://reqres.in/api/users
. This is our "request URL" - Next, select the
POST
HTTP method as shown in the screenshot above. - Next, we need to paste in our request body. Use the sample below, and paste this into the section with the placeholder:
{"key": "value"}
.01: { 02: "name": "Morpheus", 03: "job": "leader" 04: } - Go ahead and hit the blue send button after that.
You should get a response body below with a 201
status code.
That is it! This is what making an API call looks like. Ok ok we did it by clicking around, usually this will be done programmatically with code that looks something like this:
01: var myHeaders = new Headers();
02: myHeaders.append("Content-Type", "application/json");
03:
04: var raw = JSON.stringify({
05: "name": "Morpheus",
06: "job": "leader"
07: });
08:
09: var requestOptions = {
10: method: 'POST',
11: headers: myHeaders,
12: body: raw,
13: };
14:
15: fetch("https://reqres.in/api/users", requestOptions)
16: .then(response => response.text())
17: .then(result => console.log(result))
18: .catch(error => console.log('error', error));
Going over the code you should spot some familiar bits like the request data, HTTP method, and request URL. Also, there are additions like the headers. We will not be touching that bit in this tutorial.
Better tools for making API calls
In the example above we used an online tool https://reqbin.com [↗]. There are other API clients such as Postman [↗] that offer a lot more out of the box. In addition to being able to perform different types of HTTP requests, it offers features such as code generation, test automation, and documentation.
You can also use CURL [↗], a terminal/command-line-based tool to make API calls.
APIs as a product
Let's talk a bit more about APIs as a channel some companies offer their product. Take an app like Gmail [↗], it has a user interface through which you can sign up and then be able to read emails you receive as well as send out emails.
Now let's take a look at a different type of email. The kind that other apps send when you sign up, make payments, etc. This type of email is known as a transactional email.
There are companies such as SendGrid [↗], Postmark [↗], ZeptoMail by Zoho [↗] that provide APIs that other companies can incorporate into their apps to send out emails programmatically.
This means we can make calls to these APIs using an API client to send out emails, isn't that cool?
Documenting APIs
As you can see some companies have products where the user interacts with them through APIs and just like how tutorials are provided for user-interface-based products, APIs too need to be documented. Heck, when it comes to user interfaces you can figure things out by clicking around. When it comes to API products however the customer will have a hard time figuring out which URL to use all by themselves.
This is what makes documenting APIs very important. There are many ways to do this. We can write a lengthy text-based documentation or tutorial that walks the user through what they need for each task they want to perform.
However, since the process of making API requests is pretty much the same for every HTTP-based API, we only need to provide the developer with information unique to our API such as the URL, method and request payload to use.
We can go a step further to describe the response payload a bit to give them an idea of what is returned to them after the request.
Every HTTP-based request requires all four parts. This means API documentation can be standardized.
This will help the customer skip having to read through a wordy document to figure out what they need for an API request.
This is what the OpenAPI specification [↗] is set out to eliminate.
What is OpenAPI?
The OpenAPI [↗] standard allows you to define/document your APIs using structured text/syntax [↗].
This structured text is based on the YAML exchange format which we will look at next.
Remember all the different things a developer needs in other to make an API request? e.g.: The URL, HTTP method, etc. You can describe all of this using the OpenAPI standard.
We will also look at this in more detail in a bit, for now, we will look at YAML, whose syntax and structure you will use when documenting your APIs using OpenAPI.
Introduction to YAML
Let's talk about YAML now. YAML is one of the many ways of organizing text-based data, which is both readable by a human and a computer. One key part of YAML is its syntax or data formatting structure.
We are going to learn about YAML files and how to write and understand them because APIs described using the OpenAPI standard are done using YAML.
YAML is nothing more than a text file that is saved along the lines of filename.yaml
or filename.yml
where filename
can be whatever name you choose e.g.: myapi.yaml
.
Within this file, you have structured text, with almost everything being a key-value [↗] pair.
For example, Let's say we want to create a YAML file per each student in a school, where the contents of the file describe the students.
We will have something like this:
Create a file for Denis denis-mensah.yaml
.
The contents of the file will look something like this.
01: firstname: Denis
02: lastname: Mensah
03: age: 24
04: course: Economics
Key-value pair
In the above YAML file you have the keys firstname
, lastname
, age
, and course
mapping to the values Denis
, Mensah
, 24
, and Economics
respectively.
All keys need to be unique. That means you can't use a key more than once.
ListsBut what if you want to capture several values that have the same key? YAML allows you to capture lists as well.
Let's look at an example. Let's say Denis speaks three languages, French, Spanish, and English, this will be:
01: firstname: Denis
02: lastname: Mensah
03: age: 24
04: course: Economics
05: languages:
06: - French
07: - Spanish
08: - English
Notice how we indent the languages under the language keys to denote a list.
You can copy the above YAML content and paste it into an online tool like Code Beautify [↗] to create a visual tree of your YAML file. This also helps confirm if your YAML syntax is correct. Also, you can have as many lists within a list.
Commenting
Like almost all programming languages YAML allows for commenting. This is the text that is added when you need to further clarify something.
Let's say our YAML file has an optional key middlename
that needs to be set to null
if the student has no middle name. We can add a comment to clarify this point.
This will be:
01: firstname: Denis
02: middlename: null # Always set the middle name to null if the student has no middle name.
03: lastname: Mensah
04: age: 24
05: course: Economics
06: languages:
07: - French
08: - Spanish
09: - English
As you can see on line 02
we added a comment pointing out how to handle middle names. Do note that all comments begin with the #
symbol.
YAML comes packed with so many other syntaxes we can use. For this tutorial which is about OpenAPI documentation, we don't need to look at all of them.
Documenting APIs with OpenAPI
Now that we have looked at YAML it's time to look at the OpenAPI specification syntax which itself is written in YAML.
So you know how I said the OpenAPI standard provides a standardized approach to documenting APIs? Well below is an example of an OpenAPI specification I created to document a fictitious API.
In the real world, this will be a specification describing a working API. Taking a look at the specification source below, you can kind of make out the different request components.
Several tools can take the OpenAPI specification below and better visualize the documentation.
An example of such a tool is the Swagger Editor [↗]. Go ahead and open this up in a different tab then copy and paste the specification below into it.
01: openapi: 3.0.3
02: info:
03: title: Simple OpenAPI template
04: description: |
05: Simple open API template
06: license:
07: name: Apache 2.0
08: url: http://www.apache.org/licenses/LICENSE-2.0.html
09: version: 1.0.11
10: servers:
11: - url: https://example.com/api/v1
12: tags:
13: - name: v1
14: description: Simple API (v1)
15:
16: paths:
17: /data:
18: get:
19: tags:
20: - v1
21: summary: Return all data
22: description: Describe this a bit more here
23: operationId: allData
24: responses:
25: '200':
26: description: successful operation
27: content:
28: application/json:
29: schema:
30: type: array
31: items:
32: $ref: '#/components/schemas/ResponseStructure'
33: '400':
34: description: Invalid status value
35: post:
36: tags:
37: - v1
38: summary: Add data
39: description: Further description for adding data
40: operationId: addData
41: requestBody:
42: description: Request Body structure required to add data
43: content:
44: application/json:
45: schema:
46: $ref: '#/components/schemas/RequestStructure'
47: required: true
48: responses:
49: '200':
50: description: Successful operation
51: content:
52: application/json:
53: schema:
54: $ref: '#/components/schemas/ResponseStructure'
55: '405':
56: description: Invalid input
57:
58: /data/{dataId}:
59: patch:
60: tags:
61: - v1
62: summary: Update data
63: description: Further description for adding data
64: operationId: updateData
65: parameters:
66: - name: dataId
67: in: path
68: description: ID of datum to update
69: required: true
70: schema:
71: type: integer
72: format: int64
73: requestBody:
74: description: Request Body structure required to update data
75: content:
76: application/json:
77: schema:
78: $ref: '#/components/schemas/RequestStructure'
79: required: true
80: responses:
81: '200':
82: description: Successful operation
83: content:
84: application/json:
85: schema:
86: $ref: '#/components/schemas/ResponseStructure'
87: '405':
88: description: Invalid input
89:
90: delete:
91: tags:
92: - v1
93: summary: Deletes a pet
94: description: delete a pet
95: operationId: deletePet
96: parameters:
97: - name: dataId
98: in: path
99: description: ID of datum to update
100: required: true
101: schema:
102: type: integer
103: format: int64
104: responses:
105: '400':
106: description: Invalid input
107: components:
108: schemas:
109: RequestStructure:
110: type: object
111: properties:
112: name:
113: type: string
114: example: "John Doe"
115: age:
116: type: integer
117: example: 24
118: status:
119: type: boolean
120: salutation:
121: type: string
122: enum:
123: - Mrs
124: - Mr
125: - Mrs
126: - Miss
127: ResponseStructure:
128: type: object
129: properties:
130: name:
131: type: string
132: example: "John Doe"
133: age:
134: type: integer
135: example: 24
136: status:
137: type: boolean
138: salutation:
139: type: string
140: enum:
141: - Mrs
142: - Mr
143: - Mrs
144: - Miss
You should get a render similar to what is shown below:
Now let's map the OpenAPI spec entry to the visual renders shown, shall we?
Info section
Let's start with the Info section, which contains information describing what the API is for and contains version numbers.
The openapi key at the very beginning defines the version of the OpenAPI specification we are using.
title and description are meant to describe the API to the user.
The version is the actual version of the API we are defining or documenting.
There is the paths key right after the info section, this marks the start of our endpoint definitions.
GET structure
Let's look at the GET endpoint definition.
The screenshot above maps the YAML to its respective rendered section.
Let's break it down to the parts of an API request...
- URL:
/data
is the URL for the request. - Method: The method in this definition is
get
(Try changing it toPOST
and see what is rendered on the right side). - request body: GET requests do not need to send data, so in this case there is no request body.
- response body: From the screenshot you can see the arrow mapping the response in YAML (left) to its render (right).
If you look closely you will notice that you can find John Doe in the render but not in the YAML script.
This is because we are using an OpenAPI reference which allows us to define data somewhere else and pull them in by referencing them.
In this case, the referencing is done using the line
'#/components/schemas/ResponseStructure'
. - Status code: The response body documents two possible status codes
200
[↗] and405
[↗].
As you can see the definition for ResponseStructure is indented under the schemas object which itself is indented under the components object. This is known as a nested structure
Definitions are very useful if you will be using some data set more than once. For example, the ResponseStructure is the same for all the request types i.e.: GET, POST, PATCH, and DELETE
POST structure
Now let's break down the POST request.
- URL:
/data
is the URL as found in the spec, this is similar to the GET request - Method: The method in this definition is
post
. - request body: Unlike the GET request the POST request has a request body which is highlighted in the screenshot above
- response body: It has a response body similar to that of the GET request
- Status code: The response body documents two possible status codes
200
and405
.
One of the best ways to learn about the OpenAPI specification is to find random APIs online and attempt to document them. You can use the specification shared in this tutorial as your basis and then modify it to suit the API you are documenting.
Hosting OpenAPI documentation
If you shared a specification with a developer they could use the Swagger tool to preview it, but is not ideal.
Once developers or technical writers are done preparing the specification, better tools are then used to display it publicly so developers can view and work with them.
Here are some examples of beautiful API documentations out there.
- Stripe docs (custom setup) [↗]
- Checkr docs [↗] built using Redocly [↗]
- Baremetrics docs [↗] built using Readme.io [↗]
Conclusion
There are many many other parts of the OpenAPI specification that we didn't look at, for example, how to document APIs that require authentication [↗], dealing with deprecations [↗], etc.
The goal of this tutorial is to introduce you to documenting APIs using the OpenAPI specification and to get you started.
The next best step is to try to use and document APIs. A simple Google search [↗] should bring up a host of public APIs you can play around with.
The entire OpenAPI specification can be found on the official site [↗]. There is no point in learning every specification syntax. The best approach is to Google whenever you have specific needs. Most Google results will point you to the right part of the specification.
For now its good bye and best of luck on your journey!
Here is another article you might like 😊 How To Add Query Parameters To An OpenAPI Specification