API-driven development with OpenAPI and Swagger

API-driven development with OpenAPI and Swagger

While you were getting your coffee, Java application development changed--again.

In a world driven by rapid change and innovation, it's ironic that APIs are making a comeback. Like the coding equivalent of New York City's subway system in the age of autonomous cars, APIs are old tech--ancient but indispensable. What's interesting is how this invisible, everyday IT architecture is being re-envisioned and used in current technology trends.

While APIs are everywhere, they've become especially prominent in their remote incarnation as RESTful services, which are the backbone of cloud deployments. Cloud services are public APIs, which are characterized by public-facing endpoints and published structures. Cloud-based apps are also trending toward microservices, which are independent but related deployments. All of these factors increase the prominence of APIs.

In this two-part article you'll learn how to put APIs at the heart of your design and development process, from concept to coding. Part 1 starts with an overview and introduces you to OpenAPI, also known as Swagger. We'll also start developing a simple BikeParts application, which we'll build out in Part 2.

What is an API?

APIs are so commonplace in software development that it's sometimes assumed that programmers simply know what they are. Rather than rely on osmosis, let's take a minute to unpack what we mean when we talk about APIs.

First, API stands for "application programming interface." An API's role is to specify how software components interact. If you're familiar with object-oriented programming, you know APIs in their incarnation as the interfaces and classes used to obtain access to underlying features of the language, or as the public face of third-party libraries and OS capabilities.

In general, we can say that APIs set and manage the boundaries between systems, as seen in Figure 1.

jw apidrivendev fig1 Matthew Tyson

Figure 1. The role of the API

So where does that leave us with API-driven development?

Cloud computing, microservices, and REST

API-driven development comes to the fore with the modern web API: a network-exposed API (NEA), where the boundary between systems is "over the wire." These boundaries are already central to web apps, which are the common point of contact between front-end clients and back-end servers. The cloud revolution has exponentially increased the importance of APIs.

Any programming activity that requires consuming cloud services (which are basically public APIs) and deconstructing systems into smaller, independent but related deployments (also known as microservices), relies heavily on APIs. Network-exposed APIs are simply more universal, more easily obtained, and more readily modified and extended than traditional APIs. The current architectural trend is to capitalize on these features.

Microservices and public APIs are grown from the roots of service-oriented architecture (SOA) and software-as-a-service (SaaS). Although SOA has been a trend for many years, widespread adoption has been hamstrung by SOA's complexity and overhead. The industry has settled on RESTful APIs as the de facto standard, providing just enough structure and convention with more real-world flexibility. With REST as the backdrop, we can create formal API definitions that retain human readability. Developers create tooling around those definitions.

In general, REST is a convention for mapping resources to HTTP paths and their associated actions. You've likely seen these as HTTP GET and POST methods. What's key is to use HTTP itself as the standard, and layer conventional mappings on top of that for predictability.

Using APIs in design

You can see the importance of APIs, but how would you use them to your advantage?

Using API definitions to drive the design and development process is an efficient way to structure your thinking about IT systems. By using API definitions from the very beginning of the software development lifecycle (concept and requirements gathering) you will create a valuable technical artifact that is useful right up to deployment, as well as for ongoing maintenance.

Let's consider how API definitions bridge the conceptual and implementation stages of development.

Requirements gathering

On the conceptual-to-implementation spectrum, requirements gathering is way over on the concept side. But even in the conceptual stage of app dev, we can start thinking in terms of APIs.

Say your system-in-design is dealing with mountain bikes--construction, parts, and so forth. As an object-oriented developer, you'd start by talking to stakeholders about requirements. Pretty quickly after that, you would be thinking about an abstract BikePart class.

Next, you would think through the web application that would manage the various bike parts objects. Soon, you would arrive at common requirements to manage those bike parts. Here's a snapshot of the requirements phase of documentation for a bike parts app:

  • The application must be able to create a type of bike part (gear shifter, brake, etc.).
  • An authorized user must be able to list, create, and make a part type active.
  • An unauthorized user must be able to list active part types, and view lists of individual part-type instances in the system.

Already you can see the outlines of services taking shape. With eventual APIs in mind, you can begin sketching out those services. As an example, here's a partial listing of RESTful CRUD services for bike-part types:

  • Create bike part type: PUT /part-type/
  • Update bike part type: POST /part-type/
  • List part types: GET /part-type/
  • Get part type detail: GET /part-type/:id

Notice how the CRUD services begin to hint at the shape of various service boundaries. If you're building in a microservices style, you can already see three microservices emerging from the design:

  • A bike-part service
  • A bike part-type service
  • An authentication/authorization service

Because I think of APIs as boundaries of related entities, I consider the microservices from this list to be API surfaces. Together, they offer a big-picture view of the application architecture. Details of the services themselves are also described in a fashion that you will use for the technical specification, which is the next phase of the software development lifecycle.

Technical specification

If you've included the API focus as part of requirements gathering, then you already have a good framework for technical specification. The next stage is selecting the technology stack you will use to implement the specification.

With so much focus on building RESTful APIs, developers have an embarrassment of riches when it comes to implementation. Regardless of the stack you choose, fleshing out the API even further at this stage will increase your understanding of the app's architectural needs. Options might include a VM (virtual machine) to host the application, a database capable of managing the volume and type of data you're serving, and a cloud platform in the case of IaaS or PaaS deployment.

You can use the API to drive "downward" toward schemas (or document structures n NoSQL), or "upward" toward UI elements. As you develop the API specification, you will likely notice an interplay between these concerns. This is all good and part of the process. The API becomes a central, living place to capture these changes.

Another concern to keep in mind is which public APIs your system will expose. Give extra thought and care to these. Along with assisting in the development effort, public APIs serve as the published contract that external systems use to interface with yours.

Documenting the API

At this stage, you will want to start capturing your APIs in formal syntax. I've listed a few prominent API standards in Table 1.

Virtually any format you choose for documenting your API should be okay. Just look for a format that is structured, has a formal spec and good tooling around it, and looks like it will be actively maintained long term. Both RAML and OpenAPI fit that bill. Another neat project is API Blueprint, which uses markdown syntax. For examples in this article we're going to use OpenAPI and Swagger.

Introducing OpenAPI

OpenAPI is currently the most common choice for creating RESTful definitions. A compelling alternative is RAML (RESTful API Markup Language), which is based on YAML. Personally, I've found the tooling in Swagger (especially the visual designer) more polished and error-free than in RAML.

OpenAPI uses JSON syntax, which is familiar to most developers. If you'd rather not strain your eyes parsing JSON, there are UIs to make working with it easier. Part 2 introduces UIs for RESTful definitions.

Listing 1 is a sample of OpenAPI's JSON syntax.

Listing 1. OpenAPI definition for a simple BikePart



"paths": {
    "/part-type": {
      "get": {
        "description": "Gets all the part-types available in the system",
        "operationId": "getPartTypes",
         "produces": [
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "Gets the BikeParts",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/BikePart"
              }
            }
          }
        }
      }
    }
}



This definition is so concise it is practically Spartan, which is fine for now. There's plenty of room to increase the detail and complexity of the API definition going forward. I'll show you a more detailed iteration of this definition shortly.

Coding from the API

Requirements gathering is done and the basic app has been spec'd out, which means you're ready for the fun part---coding! Having a formal API definition gives you some distinct advantages. For one thing, you know what endpoints the back-end and front-end developers need to create and code against, respectively. Even if you are a team of one, you'll quickly see the value of an API-driven approach when you begin coding.

As you build out the application, you'll also see the value of using APIs to capture the back-and-forth negotiation between development and business. Using API tools will speed up both applying and documenting code changes.

More granular specs and actual coding may require greater detail than the terse definition in Listing 1. Additionally, larger and more complex systems could merit capabilities that will scale, like document references. Listing 2 shows a more fleshed out example of the BikePart API.

Listing 2. Adding detail to the BikePart API definition


  "paths": {
    "/part-type": {
      "get": {
        "description": "Gets all the part-types available in the system",
        "operationId": "getPartTypes",
        "produces": [
          "application/json"
        ],
       "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "maximum number of results to return",
            "required": false,
            "type": "integer",
            "format": "int32"
          }
        ],
        "responses": {
          "200": {
            "description": "part-type listing",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/PartType"
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          }
        }
}

Notice that the "paths" entry contains a collection of URLs. These URLs specify the paths mapped by the API. Each URL contains a collection of HTTP methods like get, post, and so on, designating a specific endpoint. Each endpoint, then, is a coordinate of a path and method. These coordinates provide a wealth of information to describe your application endpoints. You can forego that information if you want (as shown in Listing 1) but it's available for cases where you need to specify your application in detail.

Now let's look closer at Listing 2.

The "/part-type" GET coordinate contains general information, like a description and an operationId. It also defines a "produces" entry, specifying what a consumer of this endpoint can expect in output.

The "parameters" entry describes the query parameters accepted by this endpoint.

"responses" contains a collection of HTTP response codes, designating the responses made explicit by the API spec for this endpoint. Here we have the "200" all-good response, and a "default" response. Both the "200" and "default" responses give a description and a "schema" definition.

The "schema" entry defines the expected return format. Knowing that the /part-type GET entry declares that it produces "application/json", you can expect that the schema will define a JSON structure.

Document references and JSON schema

The "type": "array" in Listing 2 tells you that this endpoint will return a JSON collection. The "items" type then says: "Here is what that collection looks like." Also note that inside "items" is a document reference.

OpenAPI and other API formats support document references. The "$ref" syntax indicates a reference to another place in the definition, and the "#" of the target indicates the document root. Therefore, both the "/part-type" GET 200 and the "/part-type" GET default elements reference the API's definitions element. This is a shortcut way of specifying their respective schemas, and it keeps these elements DRY.

Listing 3 shows a simple reference for the PartType snippet.

Listing 3. A simple OpenAPI schema


"definitions": {
    "PartType": {
        "type": "object",
        {
            "required": [
                "id"
            ],
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int64"
                }
                "name": {
                    "type": "string"
                }
            }
        }
    }
}

Here you see how a simple JSON schema can be. What's more, this schema is reusable. You could even re-use it as a subsection of another schema. Like endpoint mappings, the schema definitions in OpenAPI can begin simply and expand to be very expressive.

Keeping APIs lean

The formalization of RESTful API definitions is somewhat controversial, with some strongly opposing the threat of bloat and overhead, which they fear encroaches on the practical, lean nature of REST. A consensus nevertheless seems to be coalescing around the use of formalized API specifications.

To be the most effective, API definitions must contribute their benefits with a minimum of overhead to coding and other activities. We want APIs to complement and not complicate development. Otherwise we risk going back to the same complexity that undermined early versions of SOA and SaaS. While large organizations and projects can support more detailed API development, leaner projects are free to use just enough API to make sense. That, in a nutshell, is the beauty of API-driven development.

Look for the second half of this article soon. We'll dig more into Swagger's capabilities, especially for assisting with coding.

IDG Insider

PREVIOUS ARTICLE

«In iOS 11, toggling Wi-Fi and Bluetooth 'off' doesn’t work. Here’s why.

NEXT ARTICLE

EVGA's 1080 Ti FTW3 graphics card is $700 today»
author_image
IDG Connect

IDG Connect tackles the tech stories that matter to you

Add Your Comment

Most Recent Comments

Our Case Studies

IDG Connect delivers full creative solutions to meet all your demand generatlon needs. These cover the full scope of options, from customized content and lead delivery through to fully integrated campaigns.

images

Our Marketing Research

Our in-house analyst and editorial team create a range of insights for the global marketing community. These look at IT buying preferences, the latest soclal media trends and other zeitgeist topics.

images

Poll

Should companies have Bitcoins on hand in preparation for a Ransomware attack?