Exploring the API-First Design Pattern

John Vester - Apr 4 '22 - - Dev Community

Article Image

From 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.

My Notebook

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:

API 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:

Create Design Document

Upon selecting the Design Document option, the next step is to provide a name. For this example, I entered box-finders-spec.yaml.

New Specification File

At this point, an empty design document opens in Kong Insomnia:

Empty Specification

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"

Enter fullscreen mode Exit fullscreen mode

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:

Connect to GitHub

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:

BoxFinders on GitHub

Others who wish to review or contribute to the specification can use the Git Clone option from the Create menu.

Create from Git Clone

Kong Insomnia Showing Complete API

With the changes noted above, Kong Insomnia shows the contents of the API:

Finished Specification

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:

Referencing the API

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
  }
]
Enter fullscreen mode Exit fullscreen mode

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:

Deploy to Dev Portal

Kong Insomnia will then ask for the connection properties before attempting a deployment for the first time:

Dev Portal Properties

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!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .