Skip to content

Commit

Permalink
Merge pull request #138 from kyyberi/feature/day73
Browse files Browse the repository at this point in the history
Final version of #73
  • Loading branch information
kyyberi committed Sep 26, 2019
2 parents 6a2c0ef + 366f29c commit f5fd5c3
Show file tree
Hide file tree
Showing 9 changed files with 522 additions and 0 deletions.
223 changes: 223 additions & 0 deletions _posts/2019-09-26-73.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
layout: post
comments: true
title: "#73 - Experimental machine-readable API Design Guide with OpenAPI spec"
date: 2019-09-25 03:32:44
image: '/assets/img/day73/blog.png'
description: "Configuration for the API design tool"
handles: ""
tags:
- DX
- 100DaysDX
- OpenAPI spec
- Swaggerhub
- API Design Guide
- machine-readable
- configuration
- Design tool

categories:
- DX
- 100DaysDX
twitter_text: "#73 - Experimental machine-readable API Design Guide with OpenAPI spec"
---

I started to run out of (theoretical) ideas for the posts so I had to start doing some experimentations. I wrote couple of days ago about [the idea of machine-readable API Design Guide](https://100daysdx.com/71/). I wanted to take the idea further.

You might remember the times when http APIs were documented in PDF files which you were given in response in email. Then came the idea of online API documentation and eventually Swagger, RAML and so on. We can now generate somewhat ok-ish documentation from machine-readable spec files.

## API Desing guides are still only for humans

What about API Design Guides? Those are not always PDF files, but some of them are just created with Gitbook or other documentation management solutions and frameworks. But this those are not machine-readable (without scraping and crawling). What if the API Design Guide would be similar to API spec files?

I wanted to explore the idea. I'm lazy and I wanted to do the experimentation in matter of hours. I decided to try OpenAPI spec and use that in defining API Design Guide. Probably a goofy idea, but something I can do without defining yet another spec format. Result might not be optimal and for sure not ready for "production". But it probably is enough to test the idea.

## Why?

I'm responsible among other things for the **design of 8+ APIs in our platform**. The amount is getting bigger and bigger. I'm still lacking a good API design editor, but I can survive with the spec driven solutions such as Swagger editor for now. I'm also responsible for the **API Design Guide** we have for the APIs. That is created and maintained as gitbook now. In the API design work I need to check things from the Design Guide occasionally.

Often the things are the same, some small part of the API desing such as parameter name needs to be checked. I find that far from optimal. I would like the Design tool to know the content of our API Design Guide.

<blockquote>In my dreams the API Design Guide is machine-readable and can be used as configuration for the API design tool. </blockquote>

Thus I wanted the explore the idea a little further. The result of the experiment is visible at [https://app.swaggerhub.com/apis-docs/kyyberi/API-Design-Guide/1.0.1](https://app.swaggerhub.com/apis-docs/kyyberi/API-Design-Guide/1.0.1) and as [yaml file](/assets/img/day73/openapi.yaml)


## Some possible benefits

If the Design Guide would be machine-readable just like API spec files are now:

- We could create automation to validate the uniformity of API family more easily
- No need to jump from one tool to another while designing APIs (to check little things)
- Easier to version and fork for others to use as well (open source thinking).
- could be used as "our platform configuration" for all APIs in a API design tool.

## Take the good old Swaggerhub in use

I created empty API spec in Swaggerhub. I could have used local swagger or online swagger editor here too, but since I'm already using swaggerhub in Platform of Trust API designs I decided to use same space for this small experiment.

I started from the obvious aspects of API Design Guide:
- Error handling
- Pagination
- Sorting
- Rate-limiting

```yaml
openapi: 3.0.0
info:
version: "1.0.1"
title: Machine readable API Design Guide experiment
description: >-
This is an experiment to see how well API Design Guide could be created with
OAS only.
paths:
/errors:
get:
summary: Error handling for GET methods
description: >-
Because we are using HTTP methods, we should use HTTP status codes.
Although a challenge here is to select a distinct slice of these codes,
and then depend on response data to detail any response errors. Keeping
a small set of codes helps you consume and handle errors consistently.
tags:
- Error handling
responses:
'400':
description: 'for when the requested information is incomplete or malformed.'
'401':
description: 'for when an access token isn’t provided, or is invalid.'
post:
summary: Error handling for POST methods
description: >-
Because we are using HTTP methods, we should use HTTP status codes.
Although a challenge here is to select a distinct slice of these codes,
and then depend on response data to detail any response errors. Keeping
a small set of codes helps you consume and handle errors consistently.
tags:
- Error handling
responses:
'400':
description: 'for when the requested information is incomplete or malformed.'
'401':
description: 'for when an access token isn’t provided, or is invalid.'
'403':
description: 'for when an access token is valid, but requires more privileges.'
```
## Overview of the API Design Guide
The all closed view of the result is like below. I used tags to group "specs for endpoints". Tags like "Error handling" and "Pagination of lists". That way I get the headers for the folding content.
<img itemprop="image" src="/assets/img/day73/spec2.png" alt="{{site.name}}"/>
## Error handling
When you click the "Error handling" rules part, it opens like below. I defined error handling with HTTP status codes to each method separately. Does it make sense is debatable or should all the error handling be all together. In this experiment those are separate since I figured it would be needed to have dedicated error codes for different methods. And besides that is more logical to me.
<img itemprop="image" src="/assets/img/day73/errors.png" alt="{{site.name}}"/>
If you open for example the GET method error handling rules, you'll see familiar codes:
<img itemprop="image" src="/assets/img/day73/opened.png" alt="{{site.name}}"/>
## Pagination
Pagination is almost in every good API which returns some kind of lists. Returning all the results in one batch would make the API slow and consume bandwidth while the consumer of the data wants to should just part of the results too. I added the two most commong parameters "offset" and "limit". I also added placeholders for the list objects in responses. To find a way to list things in similar fashion in all APIs might be hard due to distinct data models, but it might be useful in general to have it there.
I added the default value for the list size (50). This could used in API design tool easily and the API designer would not need to check from separate document what was the default value.
```yaml
/pagination:
get:
summary: How to paginate
description: >-
Returning huge amount of list entries in response results to bad Developer eXeprience (long response time for example). Thus you should paginate long lists to smaller chunks.
<br/><br/>
<li>Default value for list size is 50.</li>
tags:
- Pagination of lists
parameters:
- name: offset
in: query
description: Offset value to start from in providing the list items
required: false
schema:
type: integer
format: int32
- name: limit
in: query
description: maximum number of results to return
required: false
schema:
type: integer
format: int32
default: 50
responses:
'200':
description: pet response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/List'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
```
And as rendered result it looks like:
<img itemprop="image" src="/assets/img/day73/pagination.png" alt="{{site.name}}"/>
## Rate-limiting
With rate-limiting I tried the OAS headers, which seemed to work pretty nicely out of the box
```yaml
/response.headers:
get:
summary: How to notify rate-limiting information in headers
description: >-
Headers for rate-limiting.
<pre>
X-RateLimit-Limit: 60<br/>
X-RateLimit-Remaining: 25<br/>
X-RateLimit-Reset: 1546841865<br/>
</pre>
tags:
- Rate limiting
responses:
'200':
description: OK
headers:
X-RateLimit-Limit:
schema:
type: integer
description: Request limit per hour.
X-RateLimit-Remaining:
schema:
type: integer
description: The number of requests left for the time window.
X-RateLimit-Reset:
schema:
type: string
format: date-time
description: The UTC date/time at which the current rate limit window resets.

```
<img itemprop="image" src="/assets/img/day73/rate.png" alt="{{site.name}}"/>


## Simple YAML reader and renderer for API Design Guide spec

Using the default rendering of Swagger resulted to pretty ok result as such. At this point I almost started to write own renderer for the API Design Guide spec file to make it look like I want, but then I ran out of time. Perhaps someone else picks the task and continues with the idea. The more mature solution most likely would need some extensions to the default OAS spec.

The result of the experiment is visible at [https://app.swaggerhub.com/apis-docs/kyyberi/API-Design-Guide/1.0.1](https://app.swaggerhub.com/apis-docs/kyyberi/API-Design-Guide/1.0.1) and as [yaml file](/assets/img/day73/openapi.yaml)

If you would like to continue the experiment let me know. Our platform would be your first "customer" and work with you on this.
Binary file added assets/img/day73/blog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img/day73/errors.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f5fd5c3

Please sign in to comment.