Documentation of APIs can be tedious and needs to cater to different use cases such as exploring, testing and coding. This creates the need for both machine-readable as well as human-readable formats, which in the latter case often needs an interactive user interface as well.
Several tools and standards have emerged over the years to integrate these different approaches. In this blog post, we are going to explore the possibilities of a common specification format for RESTful APIs, OpenAPI.
The OpenAPI initiative emerged as an Open Source project from the Swagger project in 2015 and is now at its latest version 3.1.
At the heart of this toolkit is a YAML file that specifies the API.
The OpenAPI specification file
The basic structure of an OpenAPI specification consist of the following:
openapi: 3.0.0
info:
description: This is an example API documentation
title: API Docs Sample
version: '1.0.0'
servers:
- url: 'https://api.example.com'
description: The Sample API
paths: # see examples below
components:
securitySchemes:
Basic:
description: basic http authentication
type: http
scheme: basic
schemas:
Date:
type: string
format: date-time
requestBodies: # see examples below
responses: # see examples below
tags:
- description: An example tag
name: Sample
In this example you see some basic info, the endpoint base URL (in this case https://api.example.com
) and a Basic HTTP authentication scheme.
These are followed by a set of endpoint paths
, definitions for schemas
, requestBodies
and responses
, and a list of tags
that are used to group certain endpoints.
Schemas
Let's start by adding some schemas for our sample resource.
OpenAPI allows you to combine several schemas into new schemas, so we will be able to apply some kind of inheritance:
schemas:
Date:
type: string
format: date-time
ResourceBase:
properties:
value:
type: number
example: 42
ResourceCreate:
allOf:
- properties:
id:
type: string
example: 00a9b9bf-c099-45aa-9278-64d6a3f22476
- $ref: '#/components/schemas/ResourceBase'
ResourceFull:
allOf:
- properties:
date:
$ref: '#/components/schemas/Date'
- $ref: '#/components/schemas/ResourceCreate'
As you can see in this example, the ResourceCreate
schema inherits from ResourceBase
and adds a property id
. The ResourceFull
schema adds an extra property date
.
Now, these schemas can be used to define request bodies and responses.
Requests
First, the request bodies. When creating a resource, we will use the ResourceCreate
schema to define the resource properties.
When updating the resource, we will use the ResourceBase
schema:
requestBodies:
RequestCreate:
content:
application/json:
schema:
$ref: '#/components/schemas/ResourceCreate'
required: true
RequestUpdate:
content:
application/json:
schema:
$ref: '#/components/schemas/ResourceBase'
required: true
Responses
Calls to resource endpoints will generate certain responses, which should also be defined:
responses:
ResponseResource:
content:
application/json; charset=utf-8:
schema:
$ref: '#/components/schemas/ResourceFull'
description: OK
ResponseNotFound:
content:
application/json; charset=utf-8:
schema:
properties:
message:
type: string
required:
- message
type: object
example:
message: resource not found
description: Not Found
ResponseUpdated:
content:
application/json; charset=utf-8:
schema:
properties:
message:
type: string
required:
- message
type: object
example:
message: resource updated
description: OK
ResponseDeleted:
content:
application/json; charset=utf-8:
schema:
properties:
message:
type: string
required:
- message
type: object
example:
message: resource deleted
description: OK
ResponseCreated:
content:
application/json; charset=utf-8:
schema:
properties:
id:
type: string
example: 00a9b9bf-c099-45aa-9278-64d6a3f22476
type: object
description: Created
Paths
Now, since we have specified all our data structures, we can map all our endpoints for the resource.
Create
paths:
/resource:
post:
security:
- Basic: []
responses:
'201':
$ref: '#/components/responses/ResponseCreated'
tags:
- Sample
description: Create a resource
operationId: postResource
requestBody:
$ref: '#/components/requestBodies/RequestCreate'
summary: Create a resource
Retrieve
paths:
'/resource/{id}':
get:
security:
- Basic: []
parameters:
- description: The unique identifier for your resource.
example: '00a9b9bf-c099-45aa-9278-64d6a3f22476'
in: path
name: id
required: true
schema:
type: string
responses:
'200':
$ref: '#/components/responses/ResponseResource'
'404':
$ref: '#/components/responses/ResponseNotFound'
tags:
- Sample
description: Retrieve an existing resource
operationId: getResource
summary: Retrieve an existing resource
Update
paths:
'/resource/{id}':
get: # see above
put:
security:
- Basic: []
parameters:
- description: The unique identifier for your resource.
example: '00a9b9bf-c099-45aa-9278-64d6a3f22476'
in: path
name: id
required: true
schema:
type: string
responses:
'200':
$ref: '#/components/responses/ResponseUpdated'
'404':
$ref: '#/components/responses/ResponseNotFound'
tags:
- Sample
description: Update an existing resource
operationId: putDevice
requestBody:
$ref: '#/components/requestBodies/RequestUpdate'
summary: Update an existing resource
Delete
paths:
'/resource/{id}':
get: # see above
put: # see above
delete:
security:
- Basic: []
parameters:
- description: The unique identifier for your resource
example: '00a9b9bf-c099-45aa-9278-64d6a3f22476'
in: path
name: id
required: true
schema:
type: string
responses:
'200':
$ref: '#/components/responses/ResponseDeleted'
'404':
$ref: '#/components/responses/ResponseNotFound'
tags:
- Sample
description: Delete a resource
operationId: deleteResource
summary: Delete an existing resource
At this point, you can use this OpenAPI specification file to generate stub code for API clients, schemas for Databases, server code for testing.
An example overview of tools and generators can be found at the OpenAPI Generator Website.
Generate interactive documentation
In this blog post, we are going to use ReDoc (see their example page for more info) to render our documentation. Let's set up a simple HTML document to embed the ReDoc viewer:
<!DOCTYPE html>
<html>
<head>
<title>API Docs Sample</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="main-content">
<div id="redoc-container"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
<script type="text/javascript">
(function() {
Redoc.init(
"specs/api-docs-sample.yaml",
{},
document.getElementById('redoc-container'));
})();
</script>
</body>
</html>
Opening this in a browser will indeed show us an interactive documentation viewer:
Example payloads
Of course this can also be customised with your own look and feel. For an example of a customised documentation viewer, take a look at the Notificare API Docs.
Conclusion
Using a structured specification format like OpenAPI, you can easily generate code and documentation for your REST APIs by using the generator tools out there.
If you have any corrections, suggestions or you simply want to know more about our API documentation, as always, you can contact us via our Support Channel