Functional API Design and Documentation
Whether you're working on a traditional REST API or building something out using AWS Gateway, eventually (read: you should be doing it from the start) you may need to start documenting your functions and endpoints.
At work we've been building out API for standardizing various automation activities, and building actions for our internal chatbot. The setup the REST API has mostly been done from a few members of our team as a lift and shift from various modules inside our NodeJS / Hubot framework. Some of the functions are simple, comparing versions of applications with healthcheck endpoints in environments, to stuff that's more complex, redeploying all applications in an environment.
As of right now, there's no standardization on the request or response structures, nor are there any example requests for new team members to pick up working on making changes to the functions, just examples on how to use it from the chat platform. The best solution on how to fix both missing examples and standardize (at most) the requests is to create an REST API Specification document. While there's a few different REST API specifications out there, the most common is Swagger / OpenAPI. This specification can be easily designed and tested in something like Swagger UI, Insomnia, or other REST design tools.
What's required to build out an specification document? A basic one is quite simple to get setup. Lets take a look:
openapi: 3.0.3
info:
title: just a simple api spec
version: 0.0.1
description: can be whatever you want, usually i expand on the title
contact:
email: some.email@example.com
servers:
- url: some.url.example.com
description: if you are using something like swagger, this will give you a place to test against.
paths:
/say_hi
get:
description: returns hello
responses:
'200':
content:
application/text:
schema:
type: string
example: "hello"In this simple example you can see that there is a single endpoint path described, /say_hi that when you hit the url along with the path, (some.url.example.com/say_hi) you could expect to get back "hello". That's really all that's happening here.
Lets take it a step further, and say you have a function behind our API that could be hit to get the status of a bunch of healthchecks at once from your servers in the NYC datacenter. The response you'd expect would be a json object list where it lists the host name and if it's up or down as follows:
{
{"host_1" : "UP"},
{"host_2" : "UP"},
{"host_3" : "DOWN"}
}Lets see what the path description would be:
paths:
/healthchecks/{datacenter}:
decscription: return the healthcheck status for the provided datacenter.
parameters:
- in: path
name: datacenter
schema:
type: string
required: true
responses:
'200':
description: successful call for datacenter healthcheck
content:
application/json:
schema:
type: object
properties:
hostname:
type: string
example: "UP"
'404':
description: datacenter was not found
content:
application/json:
schema:
type: object
properties:
datacenter:
type: string
example: "San_Diego"
message:
type: string
example: "Could not find datacenter"It takes some time to plan out what your response objects may be, so don't be afraid to change your spec as you go along. In this case we've also defined what the object might look like if you want a different responce for when the datacenter is not found.
There's a few things you need to keep in mind as your are building your API and designing he responses:
-
Try to work on the path documentation as your going along. It doesn't need to be finished right from the start. If you know the post object is going to be a json object, but don't know how many fields it's going to have, you can have it commented out and build it as you go along.
-
Your input and output may change! Don't feel stuck because what you thought your input or output was going to look like isn't what ended up being what you invisioned. Let your specification grow with your api
- At work our API was originally designed to drive the complex functions for the chatbot, and thus, the response objects were originally designed to be easier to inject inside the chatbot framework. As we decided it was easier to leverage the API for some jenkins jobs instead of duplicating functionality we've had to adapt and are now serving responses based off of header information.
-
Descriptions of what the path is for is immensely helpful. Someone who is looking at your swagger page, or the actual document itself should be able to figure out exactly what is going on and what is being returned.
-
Just as important, be consistent on your naming conventions for things. If you have a path / function where the input is environment and service, in another that has just environment, don't have the input be env and env_name. Keep them the same!
This is probably a very glossary approach, but this was the process i tried to follow as I created the one for my team.