Exploring the API-First Design Pattern
Learn how the API-first design pattern is a carbon copy of the successful writing approach that John Vester has leveraged for several years.
Join the DZone community and get the full member experience.
Join For FreeFrom a career perspective, the two things I appreciate the most are solving problems through technology and creating technical publications. The former often drives the latter: results presented in inspired-based publications are derived from a recent problem that I had successfully solved.
Along my three-decade journey, I also discovered that I enjoy making lists. Early in my career, I used quad-ruled notebooks to establish lists to work from daily.
Of course, those graphically-lined necessities have fallen aside in favor of using derived lists maintained on Trello or JIRA boards. For smaller projects, I tend to lean toward using the Sublime and Atom text editors. What has not changed is my preferred approach to work from a list.
Moving From Lists to Outlines
For larger initiatives, simply working from a list does not provide enough details. This is often the case with ideas I have for a new publication.
In those cases, I introduce sub-items to each core list item. While it is possible to include child items at the sub-item level, I often find that I am getting into the weeds and not focusing on the broader landscape.
Once there is good coverage for each item on the list, I figure out how best to order each item. During this step, the list actually becomes an outline, where there is a sequence established to my collection of thoughts or ideas.
The resulting product is what I have ended up using for every publication I have submitted since 2015. However, the outline concept has not been limited to my technical writing. I have also employed this very same approach when building APIs.
The API-First Design Pattern
Janet Wagner noted that an API-first approach “treats APIs with importance, as reusable and easily accessible products that client applications consume. API-first means designing products around an API from the ground up, rather than building a product and adding an API later.”
After establishing API standards, the API-first design pattern allocates time at the beginning of the process to produce a solid API design, focusing on high-level characteristics, which include:
Resource path (URI)
Operation/Request types (GET, PUT, POST, PATCH, DELETE)
Inbound parameters/payload
Outbound response codes (1xx, 2xx, 3xx, 4xx, 5xx)
Outbound response payload (type and data model)
Additional meta-data (description, contact, terms of use)
When developing these specifications I recommend the following lifecycle:
In the illustration above, the first step is to listen to the needs of the API and to repeat any requirements back to the product owner driving the underlying business rules. After achieving that understanding, the design phase begins by leveraging a standards-based specification (like OpenAPI). Finally, consumers of the API can preview it.
Often, the cycle does not end there, as questions and challenges arise from the preview phase. At that point, it will require additional time to share these concerns with the product owner, who will provide additional information. At that point, the cycle begins with the goal of providing a more-refined API specification.
By employing an API-first design pattern, a single-source-of-truth artifact is documented before writing a single line of code. The specification for the API will live outside the source systems that produce the actual API in a manner that can be easily cataloged and consumed by future client and service engineers.
Now, let’s get started and work through a simple use case.
Box Finders API: An Example Use Case
After recently moving into our new home, I thought of a use case to illustrate the API-first design pattern. My idea centers around cardboard boxes used for moving one’s possessions.
Boxes are necessary before you start packing to move into a new home. If you’re like me, finding boxes that are still in good shape and for little or no cost is an ideal situation. Then, after the move is complete, finding someone to take your gently-used boxes is just as important to avoid a not-so-attractive cardboard display in the corner of your garage.
Enter the Box Finders API, where customers perform the following operations:
GET /boxes - returns a list of available box collections
GET /boxes/{id} - retrieves a single box collection
POST /boxes - adds a new box collection
PUT /boxes/{id} - updates an existing box collection
DELETE /boxes/{id} - removes an existing box collection
For simplicity, let’s assume the Box Finders API requires the following attributes:
Id - unique numeric identifier for the box collection
Name - the name of the person to contact
Phone - the contact’s phone number
Available - the number of available boxes
Using Kong Insomnia with an API-First Design
Since I had not used the Kong Insomnia application before, I thought I would give it a try for proving out the API-first design pattern.
Once installed, getting started with an API-first design begins with using the Create menu on the left-side of Kong Insomnia:
Upon selecting the Design Document option, the next step is to provide a name. For this example, I entered box-finders-spec.yaml.
At this point, an empty design document opens in Kong Insomnia:
Now, we are ready to start designing the Box Finders API.
Adding General Information
At the top of the file, you can add some general information about the specification. I used OpenAPI version 3.0.0:
openapi: 3.0.0
info:
version: "0.0.1"
title: "Box Finders API"
Adding Schema Information
Based upon the current understanding of the Box Finders API, I added the data models (also known as schema components) to the bottom of the file:
components:
schemas:
Boxes:
type: array
items:
$ref: "#/components/schemas/Box"
Box:
type: object
properties:
id:
type: number
description: Unique identifier
name:
type: string
description: Person with boxes to give away
phone:
type: string
description: Phone number of person with boxes to give away
available:
type: number
description: Number of available boxes
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
Taking this approach will keep the data side of the contract defined in one central location of the API specification.
Adding /boxes
URIs
With the general information and schema sections in place, we can add the /boxes URIs as path items following the OpenAPI standards:
paths:
/boxes:
get:
summary: "Lists all available box collections"
responses:
"200":
description: "200 OK, all box collections"
content:
application/json:
schema:
$ref: "#/components/schemas/Boxes"
post:
summary: "Adds a new box collection"
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Box"
required: true
responses:
"201":
description: "201 Created, a new box collection was added"
content:
application/json:
schema:
$ref: "#/components/schemas/Box"
In this example, I’ve added URIs to retrieve all box collections and create a new box collection under the paths section.
Adding /boxes/{id}
URIs
Next, I added the ability to retrieve, edit, or delete a single box collection as additional paths, still following the OpenAPI standard:
/boxes/{id}:
parameters:
- in: path
name: id
schema:
type: number
required: true
description: "id of the box collection"
get:
summary: "Retrieves a box collection by identifier"
responses:
"200":
description: "200 OK, box collection by identifier"
content:
application/json:
schema:
$ref: "#/components/schemas/Box"
"404":
description: "404 Not Found, box collection does not exist"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
put:
summary: "Updates a box collection by identifier"
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Box"
responses:
"202":
description: "202 Accepted, updated box collection"
content:
application/json:
schema:
$ref: "#/components/schemas/Box"
"404":
description: "404 Not Found, box collection does not exist"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
summary: "Deletes a box collection by identifier"
responses:
"204":
description: "204 No Content, deleted box collection"
"404":
description: "404 Not Found, box collection does not exist"
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
At this point, the 0.0.1 version of the Box Finders API is complete. However, before moving forward, I felt like it was a good time to store this information inside a git-based repository. This way, the design is not limited to existing on my local machine.
Connecting to GitHub
The Kong Insomnia client allows integration with an existing git-based repository. I created a new repository at the following URL:
https://github.com/johnjvester/box-finders-api
Next, I created a personal access token to create read/write access to the GitHub repository. Then, I used that resulting token to set up access within Kong Insomnia for the Box Finders API specification:
At this point, I committed my changes and pushed them to GitHub. Once completed, the changes from Kong Insomnia are now available for others to pull into their own client:
Others who wish to review or contribute to the specification can use the Git Clone option from the Create menu.
Kong Insomnia Showing Complete API
With the changes noted above, Kong Insomnia shows the contents of the API:
At this point, the Box Finders API specification can be consumed by service and consumer developers without writing a single line of code.
Using Box Finders API
For cases where a consumer of the Box Finders API wants to develop their application while the feature team is developing the Box Finders RESTful service, Kong Insomnia provides the entire contract for the new client to utilize:
When making a GET request to the /boxes
URI, the service will return a list of Box objects:
[
{
"id": 0,
"name": "string",
"phone": "string",
"available": 0
}
]
Using the API-first design pattern, every client or service tied to the Box Finders API can begin their development before writing a single line of code for the Box Finders service.
Deploying to Kong Dev Portal
If you’ve set up an account with Kong Konnect to use the Kong Dev Portal, you can use the Deploy to Dev Portal menu option on the Box Finders specification menu:
Kong Insomnia will then ask for the connection properties before attempting a deployment for the first time:
Conclusion
Since last year, I have been trying to live by the following mission statement, which I feel can apply to any IT professional:
“Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”
- J. Vester
In this article, I used Kong Insomnia to create a standardized API specification that can be fully documented, vetted, and communicated before writing any source code. This allows teams to collaborate on the design and make changes without requiring any costly service-tier updates. Clearly, this embraces my personal mission statement.
Kong Insomnia is a product that allows feature teams to remain focused on extending intellectual property value, contributing to the bottom line positively. Taking things a step further, you can easily deploy the results from Kong Insomnia to Kong Konnect, which centralizes API specifications for other consumers to find and utilize in their applications.
In my personal journey, I created lists to help with my daily assignments. In most cases, those lists became outlines, which then led to designs and specifications. The API-first design pattern was a natural progression for me and is certainly a concept I appreciate and embrace.
If you are interested in the original source code for this publication, everything noted above can be found at the following link:
https://github.com/johnjvester/box-finders-api
Have a really great day!
Opinions expressed by DZone contributors are their own.
Comments