Example
We can compare the language-oriented approach with a format-oriented approach that uses OpenAPI as the format. In this example, we'll build an API to keep track of automobiles.
The high-level API design
For this example, consider an API that allows a consumer to do the following:
- Retrieve a collection of automobiles at the URL
/automobiles
using theGET
HTTP method, which returns an array of automobile representations in JSON. The status code should be200
. - Retrieve a single automobile item at
/automobiles/{id}
using theGET
HTTP method, which returns an automobile representation in JSON. The status code should be200
.
In addition to these details, the automobile schema should include the properties id
, make
, model
, and year
, which should all be strings.
Extracting the rules for an API governance model
We'll define a few rules regarding how we design APIs. This will be a small set compared to a full API governance model. The applicable rules for this design might be as follows:
- All collection URLs should be plural (e.g.
/automobiles
) - All item URLs should be the collection URL with an ID after it (e.g.
/automobiles/{id}
) - Use the
GET
method to retrieve the collection and item - Return a
200
on successful requests
An organization might define these rules in a human-readable document and require people to use them when designing APIs. These rules—along with many others—become the foundation for the API governance model.
How would we define this in OpenAPI?
In OpenAPI, we'd define the API this way:
openapi: 3.0.0
info:
version: 1.0.0
title: Automobile API
paths:
/automobiles:
get:
operationId: list-automobiles
responses:
200:
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/AutomobileCollection"
/automobiles/{automobile_id}:
get:
operationId: retrieve-automobile
parameters:
- name: automobile_id
in: path
required: true
schema: {type: "string"}
responses:
200:
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/AutomobileItem"
components:
schemas:
AutomobileItem:
type: object
properties:
id: {type: "string"}
make: {type: "string"}
model: {type: "string"}
year: {type: "string"}
AutomobileCollection:
type: array
items:
$ref: "#/components/schemas/AutomobileItem"
Here we can encounter the challenge: how do we know that we defined the API correctly based on the API governance model? Did we use the correct URL patterns for collections and items? Did we use the right status codes? We'd need to codify the rules of our API governance model so that we can apply it to any OpenAPI document we write. This would require a linting tool to make these kinds of checks and provide feedback during the design process.
Designing an API this way requires a few things from us:
- We have to know OpenAPI
- We have to be able to map our API conversation into OpenAPI
- We have to know the API governance model and understand how to design APIs that follow the rules
- We'd need tools that could apply the API governance model rules to an OpenAPI document
To create a successful API program that uses OpenAPI, an organization needs to train people on OpenAPI, define API governance model rules, acquire tools, then create a path for teams to take to adopt this new workflow.
How would we define this API using a language-oriented approach?
First, consider the following: what are the essentials to the design? Many of the technical details like URL patterns, HTTP methods, and status codes are fixed. The dynamic parts of the design are the resources and their properties.
Our first task would be to design a DSL. In an ideal situation, we'd create this prior to the API design process. For the current needs, we might design a DSL that includes the resource name, the resource's operations and properties, and each property's types. The DSL might look like this:
resources:
- name: <resource-name>
operations:
- <operation-name: create, read, update, delete, or list>
properties:
<property-name>: <property-type>
Using this DSL, we'd come up with a design for our API that looks like this:
resources:
- name: automobile
operations:
- list
- retrieve
properties:
make: string
model: string
year: string
The OpenAPI document we wrote above by hand was 43 lines. This document using a DSL is nine. Where did all those other details go? Where are the status codes and schemas? What do the URLs look like? Where is the id
property? These are details that can be left to the tooling that generates the OpenAPI document from the DSL. This is because we know how to generate the OpenAPI operations, include all the right status codes for the right operations, generate URLs based on the resource name, and create the schemas from the properties including the common properties like id
. We can do this because the details around a large portion of the OpenAPI document are fixed. There's no imperative for people to learn how to write these details in OpenAPI—software can do the work for them.
It's important to note again that this example doesn't provide a full DSL. It's minimal and lacking a lot of features you'd need to build complete APIs. But it shows how you can start small and build up as the needs arise.
You can view a working example of this on GitHub at https://github.com/smizell/language-oriented-example.