4
Describing an API with an API description format

This chapter covers

  • What an API description format is
  • How to describe a REST API with the OpenAPI Specification (OAS)

In the previous chapters, we explored how to design a basic programming interface using the information collected in the API goals canvas. We identified the resources, actions, parameters, and responses for our example Shopping API. We also designed the API’s data. But we did all of this using box-and-arrow diagrams and some tables.

Such figures and tables are always useful for brainstorming and getting an overall idea of how the API’s goals can be transposed into a programming interface. But when it comes to describing precisely a programming interface, and especially its data, it is simpler and more efficient to use a structured tool like an API description format. Being code-like and a standardized description of an API, this offers many advantages:

  • It notably facilitates the sharing of your design with anyone involved in your project.
  • It can be easily understood by people knowing this format and by any API documentation tools (among many others).

The OpenAPI Specification (OAS) is a popular REST API description format. In this chapter, we will walk through its basics in order to uncover the benefits of using such a format.

4.1 What is an API description format?

An API description format is a data format whose purpose is to describe an API. Figure 4.1 shows how the programming interface for the add product to catalog goal can be described in a simple text file using such a format.

04-01.png

Figure 4.1 Describing a programming interface with an API description format

This text file uses data to tell the same story as our tables and hand-drawn box-and-arrow diagrams. According to the description at the top of the document, the /products resource represents the catalog. It contains a POST HTTP method that we can use to add a product to catalog. This POST operation needs a requestBody containing a name and a price. The description even provides additional information, such as example values for the properties. Such a file can be written quickly using a simple text editor. But, most importantly, because this file contains structured data, programs can read it and easily transform it into something else. A basic use is to generate reference documentation that describes all of an API’s goals. You can share this with all the people involved in the project so they have a good overview of the design being created.

That sounds interesting, and it’s only the tip of the iceberg. This simple text file uses the OAS. Let’s take a closer look at this popular REST API description format before discussing the usefulness of such a format and when to use it while designing APIs.

4.1.1 Introducing the OpenAPI Specification (OAS)

The OpenAPI Specification (OAS) is a programming language-agnostic REST API description format. This format is promoted by the OpenAPI Initiative (OAI), which “…was created by a consortium of forward-looking industry experts who recognize the immense value of standardizing on how REST APIs are described. As an open governance structure under the Linux Foundation, the OAI is focused on creating, evolving and promoting a vendor neutral description format.” The OAS (https://www.openapis.org) is a community-driven format; anyone can contribute to it through its GitHub repository (https://github.com/OAI/OpenAPI-Specification).

Figure 4.2 shows a very basic OAS 3.0 document. This OAS document is written using the YAML data format.

04-02.png

Figure 4.2 An OAS document describing the search for products goal of the Shopping API

Such a basic OAS document provides general information about the API, such as its name and version. It describes the available resources (identified by their paths) and each resource’s operations (or actions, as you saw in the previous chapter) identified by HTTP methods, including their parameters and responses.

An OAS document can be written in YAML or JSON, so which one should you use? As you will be writing the documents yourself, I recommend using the YAML format, which is, in my opinion, easier to read and write.

Though this book focuses on OAS, other REST API description formats exist; the most notable OAS competitors are RAML and Blueprint. I choose to focus on OAS, not only because I use it everyday, but also because it is community-driven and widely used. Note, however, that this book is not called OpenAPI Specification in Action.

OAS and its ecosystem offer many features and, while we will discover some of them throughout this book, it won’t be possible to cover them all. Once you are familiar with what is presented in this book, I recommend you read the OAS documentation (https://github.com/OAI/OpenAPI-Specification/tree/master/versions) and use the OpenAPI Map (https://openapi-map.apihandyman.io), a tool I created to help you find your way through the specification.

You’ve seen that an API description format such as the OAS lets you describe an API using a text file containing some kind of structured data. But you could also use a word processor or spreadsheet document to do the same thing. I urge you to avoid doing so; let’s see why.

4.1.2 Why use an API description format?

Indeed, you could describe a programming interface using a word processor or spreadsheet document. You could also easily share such a document with others. But can you version it easily? Can you generate documentation from it? Can you generate code from it? Can you configure API-related tools with it? Can you … ? I could probably cover an entire page with such questions. Using an API description format has benefits throughout the API lifecycle, and especially during the design phase. It benefits not only the API providers but also API consumers.

Describing APIs efficiently is like writing code

An OAS document is a simple text file that can be easily stored in a version control system such as Git, just like code. It is therefore simple to version it and track modifications while iterating on the API design.

An OAS document has a structure that helps to describe a programming interface more efficiently. You have to describe resources, operations, parameters, and responses. You can define reusable components (such as a data model, for example), avoiding the painful and risky art of maintaining copy/pasted pieces of API descriptions.

Speaking of writing an OAS document, you can use your favorite text editor, but I recommend using an editor that specifically handles this format. You can use the online Swagger Editor (http://editor.swagger.io) shown in figure 4.3.

04-04.png

Figure 4.3 The Swagger Editor, an online OAS editor

Because OAS is a machine-readable format, this editor offers features like autocompletion and document validation, and the right-hand panel shows a useful rendering of the edited OAS document. To see a human-friendly representation of the data structures used as parameters or responses, the Model and Example Value views are especially useful. This editor is an open source project, and its source code is available on GitHub (https://github.com/swagger-api/swagger-editor).

This online editor is great because you only need a browser to run it, but it can be cumbersome to constantly download or copy and paste the edited file to actually save or open it. I personally use the Microsoft Visual Studio Code editor along with the Swagger Viewer extension (https://marketplace.visualstudio.com/items?itemName=Arjun.swagger-viewer), which supplies a SwaggerUI-based preview panel, and the openapi-lint extension (https://marketplace.visualstudio.com/items?itemName=mermade.openapi-lint), which provides autocompletion and validation. This configuration provides the same experience as the online editor, and you work directly with your files.

Note that there are API design tools that let you describe a programming interface without writing any code. Some of them propose interesting features like collaborative working. If you want to use such a tool, that’s fine; just check that your work can be exported to a known and used API description format. But even though such tools exist, it’s still worthwhile to know how to write an OAS document. You need almost nothing to use such a format, and one day you might want to build your own tooling around this format.

Sharing API descriptions and documenting APIs easily

An OAS document can be easily shared with others even outside your team or company to get feedback on your design. Unlike a specific internal format known only by a few, the OAS format is widely adopted. People can import the document into the online Swagger Editor or many other API tools. Alternatively, to avoid bothering them with the OAS document itself, you can provide access to a ready-to-use, human-friendly rendering. An OAS document can be used to generate API reference documentation that shows all the available resources and operations. You can use the Swagger UI (https://github.com/swagger-api/swagger-ui) for this, which shows the OAS document as in the right-hand pane of the Swagger Editor.

There are non-Swagger OpenAPI tools, too. For example, as an alternative to Swagger UI, you can use a tool such as ReDoc (https://github.com/Rebilly/ReDoc), which is also open source. Figure 4.4 shows the ReDoc OpenAPI tool.

04-05.png

Figure 4.4 An OAS document rendered in ReDoc

And one final note: to create API documentation, you will discover advanced uses of OAS in chapter 12.

Generating code and beyond

Once an API is described with an API description format, the implementation code can be partially generated from it. You will get an empty skeleton of source code that you can also use to generate a working mockup. Consumers can also take advantage of such machine-readable API descriptions to generate code to consume the API. And such a format can also be used by API testing or security tools, and many other API-related tools. For example, most API gateway solutions (proxies made to expose and secure APIs) can be configured using an API description file such as an OAS document.

These examples alone show how an API description format is more efficient than a word processor or spreadsheet document. And OAS documents can be used in many other ways.

4.1.3 When to use an API description format

Before starting to describe an API using an API description format, however, you must be sure you’re doing so at the right time, as shown in figure 4.5.

04-06.png

Figure 4.5 When to use an API description format

An API description format is made to describe a programming interface. Therefore, it must not be used while identifying the API’s goals. As you learned in chapter 3, designing the programming interface first without having a clear view of what the API is supposed to do is a terrible idea! And an API description format must not be used while identifying the concepts behind the goals; that’s still a little bit too early. Sure, it’s the first step of designing the programming interface, but during this phase you are still not dealing with a real programming interface. An API description format definitely must be used, however, when designing the programmable representation of goals and concepts, and the data.

When you design a REST API, you can start to use the OAS when you design the resource paths and choose the HTTP methods describing actions. It is possible to create a minimal file containing only these elements. Once that is done, you can complete the document by describing the API’s data. As you will discover in the following section, describing all of this will be far simpler and more efficient using an API description format than drawing tables, arrows, and boxes. But do not forget that all people involved in the project and who need to actually see the API design might not be familiar with a code-like format such as the OAS, so always provide a way to get a human-friendly representation of the file you are working on. With that in mind, let’s see how we can describe REST API resources and actions using the OAS.

4.2 Describing API resources and actions with OAS

As shown in figure 4.6, when we transposed API goals into a programming interface in chapter 3, we identified resources and actions and represented them with paths and HTTP methods.

04-07.png

Figure 4.6 From figure to OAS document

We used boxes and arrows to describe this programming interface, but this can also be done in a more structured way using the OAS. The resulting document will contain exactly the same information as the corresponding figure, but this time the information will be presented as data in a structured document. Let’s get started now on our document describing the Shopping API.

4.2.1 Creating an OAS document

The following listing shows a minimal but valid OAS document. This document is written using OAS version 3.0.0. It describes an API named Shopping API in its 1.0 version.

Listing 4.1 A minimal but valid OAS document

openapi: "3.0.0"  
info:  
  title: Shopping API
  version: "1.0"
paths: {}  

①   OAS version

②   API’s general information

③   Empty paths

The structure of OAS documents can evolve from one version to another, so parsers use the openapi version to adapt their parsing accordingly. Note that both the specification (openapi) and API (info.version) version numbers must be surrounded by quotes. Otherwise, OAS parsers will consider these as numbers and document validation will fail because these two properties are supposed to be strings.

The listing shows the paths property only to make the document valid. (If it’s not present, the parser reports an error.) The paths property contains the resources available to this API. We can set its value to {} for now—this is how you describe an empty object in YAML. A nonempty object (like info) does not need the curly braces. Next, we will start to fill the paths property by adding a resource.

4.2.2 Describing a resource

As shown in figure 4.7, while working on the goals search for products and add product to catalog, we identified a catalog resource. We chose to represent it with the /products path.

04-08.png

Figure 4.7 Adding a resource to the OAS document

In order to describe this resource in the OAS document, we must add the /products path into the paths property as shown in figure 4.7 (don’t forget to remove the empty curly braces!). We’ll also describe what this resource is (The products catalog) using the description property of the resource, as shown in the following listing.

Listing 4.2 Describing a resource

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:  
  /products:  
    description: The products catalog  

①   API’s resources

②   Resource’s path

③   Resource’s description

The description is not mandatory, but providing descriptions of your API’s resources will be useful throughout the API lifecycle. It’s like when you code: the code can be understandable on its own, but comments or JavaDoc, PHPDoc, JSDoc, or <your favorite language>Doc annotations about its use will always be welcomed by other people who read your code or the documentation generated from it.

Because an API by definition will be used by others, it is really important to take advantage of the documentation possibilities of API description formats. During this design phase it is especially useful to keep a link between your earlier work, like the API goals canvas or concept identification, and the programmable representation. It might also help people with whom you share this design to understand it more easily (/products being a catalog might not be obvious to everyone).

A resource described within an OAS document must contain some operations. Otherwise, the document is not valid. The catalog resource is used by two goals: search for products and add product. Let’s see how we can describe them as operations in the OAS document.

4.2.3 Describing operations on a resource

We can add to our document in order to provide all the information for each goal we identified by the end of section 3.2. For each one, we know which HTTP method it uses, and we have a textual description of its inputs and outputs, as shown on the left side of figure 4.8.

04-09.png

Figure 4.8 Adding an action to a resource

To represent search for products, we chose to use the GET HTTP method on the catalog resource represented by the /products path. This action uses a free query parameter and returns products matching the query. To add this action to the /products resource, we use a get property. We can also use documentation features to provide more information about this GET /products operation. We’ll set the summary property to Search for products and the description to Search for products in catalog using a free query parameter, as shown in the following listing.

Listing 4.3 Describing an action on a resource

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:  
    description: The products catalog
    get:  
      summary: Search for products  
      description: |  
        Search for products in catalog
        using a free query parameter

①   Resource

②   Action’s HTTP method

③   Action’s short description

④   Action’s long description

The summary property is a short description of the action, without details. The goal defined in the API goals canvas is usually perfect to use here. There is also a description property, which can be used to provide a more detailed description of the action. Here we use it to indicate that this action is using a free query parameter. Note that the description property is multiline. This is a YAML feature: to be multiline, a string property must start with a pipe (|) character.

In an OAS document, an operation must describe at least one response in its responses property, as shown in listing 4.4. For now, we will use this mandatory response to provide an informal description of the output of the search for products action. We’ll add a responses property containing a "200" response (for HTTP status code 200 OK), whose description is Products matching free query parameter.

Listing 4.4 Describing an action’s responses

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:
    description: The products catalog
    get:
      summary: Search for products
      description: |
        Search for products in catalog
        using a free query parameter
      responses:  
        "200":  
          description: |  
            Products matching free query parameter

①   Action’s response list

②   200 OK HTTP status response

③   Response’s description

As mentioned, an action’s possible responses are described in its responses property. Each response is identified by its HTTP status code and must contain a description property. The "200" property stands for the 200 OK HTTP status, which tells the consumer that everything went fine. (Did you notice the quotes around the status code? They are needed because YAML property names must be strings and 200 is a number.) The response’s description property states that if everything went OK, the Products matching free query parameter are returned. We will explore the possible responses and HTTP status codes returned by an action in more depth in chapter 5.

Now that we have added this action to the /products resource, our OAS document is valid. But we’re not done yet. There is a second action on this resource: add product. We chose the HTTP method POST to represent the add product action on the catalog resource. It takes some product information and returns the product added to the catalog (see figure 4.9).

04-10.png

Figure 4.9 Adding another action to a resource

To add this action, we proceed exactly like we did previously. The following listing shows how to do this.

Listing 4.5 Describing another action

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:  
    description: The products catalog
    get:
      summary: Search for products
      description: |
        Search for products in catalog
        using a free query parameter
      responses:
        "200":
          description: |
            Products matching free query parameter
    post:  
      summary: Add product  
      description: |  
        Add product (described in product info
        parameter) to catalog
      responses:  
        "200":  
          description: |  
            Product added to catalog

①   Resource

②   Action’s HTTP method

③   Action’s short description

④   Action’s long description

⑤   Action’s response list

⑥   200 OK response

⑦   200 OK response’s description

We add a post property inside the object describing the catalog resource identified by the /products path. We set its summary property to Add product and its description to Add product (described in product info parameter) to catalog. We add a "200" property inside responses and set its description to Product added to catalog.

The /products resource’s post operation is now described within the OAS document. Anyone looking at this document can tell what this operation does by reading its summary, description, and response description. As shown in figure 4.10, this document contains the same information we identified in section 3.2.

04-11.png

Figure 4.10 OAS document corresponding to the initial figure

Congratulations! You now know the basics of how to describe a resource and its actions using the OAS. Even if it is not yet complete, such a description already provides interesting information about the API. We have a formal and structured description of the resource’s path and HTTP methods, and we are able to determine which goal corresponds to which action and how they work, thanks to the description properties.

But this document only provides a vague description of each operation’s inputs and outputs. In the previous chapter, we designed these inputs and outputs in detail. Let’s see now how to complete this document by describing those.

4.3 Describing API data with OpenAPI and JSON Schema

In the previous chapter, when we designed the programming interface matching the identified goals, we did not stop after designing the resource paths and choosing the HTTP methods. In section 3.3, we fully described the actions' parameters and responses, including descriptions of the data organization and properties (figure 4.11).

04-12.png

Figure 4.11 From figures and tables to a detailed OAS document

The OAS relies on the JSON Schema specification (http://json-schema.org) to describe all data—query parameters, body parameters, or response bodies, for example. JSON Schema aims to describe data formats in a clear human-readable and machine-readable way. It can also be used to validate JSON documents against a JSON schema, a data description made with JSON Schema. This format can be used independently from OAS to describe and validate any type of JSON data.

In this chapter, JSON Schema refers to the JSON Schema specification, whereas a JSON schema is an actual schema, a description of data. Note the difference in capitalization. Let’s see how we can describe API data using the OAS and JSON Schema. We’ll start with the search for products query parameter.

4.3.1 Describing query parameters

To search for products, API users have to provide a free query parameter to indicate what they are looking for (figure 4.12). In the previous chapter, we decided that this parameter would be a query parameter named free-query. To search for products using the API, a consumer would issue a GET /products?free-query={free query} request (for example, GET /products?free-query=book).

04-13.png

Figure 4.12 The search for products free query parameter

To describe this parameter, we add a parameters property inside the get operation of the /products resource, as shown in the following listing.

Listing 4.6 Describing parameters

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:  
      get:  
        summary: Search for products
        description: |
          Search for products in catalog
          using a free query parameter
        parameters:  
         [...]
        responses:
          "200":
            description: |
              Products matching free query parameter

①   Resource

②   Action

③   Action’s parameters list (except body)

When an action on a resource needs parameters other than body parameters, they are described in the action’s parameters property. In this case, to describe the parameter, we set its name to free-query, as shown in the following listing.

Listing 4.7 Describing a query parameter

parameters:
  - name: free-query  
    description: |  
      A product's name, reference, or
      partial description
    in: query  
    required: false  
    schema:  
      type: string  

①   Parameter’s name

②   Parameter’s description

③   Parameter’s location

④   Whether a parameter is mandatory

⑤   Parameter’s data structure description

⑥   Parameter’s type (string)

We indicate that the parameter is located in the query but is not required, and that its data structure is described in schema. This schema simply indicates that this parameter’s type is string. We also provide some additional information in its description to tell that its value could be A product’s name, reference, or partial description.

The parameters property is a list or array. In YAML, each element of a list or array starts with a dash (-). To describe a parameter, we need at least three properties: name, in, and schema. This parameter’s description also contains two optional properties: required and description.

The parameter’s name is the name that will be shown in the path (/products?free-query={free query}). The in property indicates the location of the parameter. Here, it’s a query parameter, so it’s located after a ? in the path.

The required property, which indicates if the parameter must be provided, is not mandatory. If required is not set, its default value is false, indicating that the parameter is optional. You are under no obligation to set it unless you need to define a parameter as required. But even though the parameter is optional, it’s always better to explicitly specify required: false. This way, you are sure that you’ve considered whether each parameter is mandatory or not.

The parameter’s data structure described in the schema property is a JSON schema. As mentioned earlier, JSON Schema is used in an OAS document to describe the API’s data—from a simple string query parameters to more complex structures used as body parameters and responses. Using JSON Schema, let’s see how we can describe a product, such as the one designed in section 3.3.1.

4.3.2 Describing data with JSON Schema

We’ll start with a very basic version of the product, as shown in figure 4.13. It’s composed of a reference, a name, and a price. The reference and name properties are of type string and price is a number.

04-14.png

Figure 4.13 A basic product description

Earlier, to describe a simple string query parameter with JSON Schema, we used type: string. Now, to describe such a product object, we have to use the type object and list its properties. Each property is identified by its name and type, as seen in the following listing.

Listing 4.8 Describing a very basic product with JSON Schema

type: object  
properties:  
  reference:  
    type: string  
  name:  
    type: string  
  price:  
    type: number  

①   This schema describes an object.

②   It contains properties.

③   Property’s name

④   Property’s type

But when we discussed designing the API’s data in the previous chapter, you learned that we must also identify which properties are required. Because the reference, name, and price properties are all mandatory, we’ll add an optional description for the sake of example (see figure 4.14).

04-15.png

Figure 4.14 A product description with required flags

Now the product is composed of mandatory reference, name, and price properties and an optional description. To indicate that in the corresponding JSON Schema, we add reference, name, and price entries into the object’s required properties list, as shown in the following listing.

Listing 4.9 Required and optional properties for product

type: object
required:  
  - reference
  - name
  - price
properties:
  reference:  
    type: string
  name:  
    type: string
  price:  
    type: number
  description:  
    type: string

①   Required properties list

②   Required properties

⑤   Optional property

The JSON Schema allows us to indicate which properties are required in an object with its required list. Any property whose name is included in this list is mandatory. Any property whose name is not in this list is considered optional. In this case, description is the only optional property.

Our schema is getting pretty accurate, but when we designed the product resource, we found that we sometimes needed to add some descriptions because the property names were not sufficient to explain their nature. Let’s add a description to the object to show that it describes A product. We can also add a description to explain what a reference is. We can even add an example to show what a product’s reference looks like. The next listing shows this.

Listing 4.10 Documenting a JSON schema

type: object
description: A product  
required:
  - reference
  - name
  - price
properties:
  reference:
    type: string
    description: Product's unique identifier  
    example: ISBN-9781617295102  
  name:
    type: string
    example: The Design of Web APIs  
  price:
    type: number
    example: 44.99  
  description:
    type: string
    example: A book about API design  

①   Object’s description

②   Property’s description

③   Property’s value example

Just like the OAS, the JSON Schema comes with useful documentation features. An object and all of its properties can be described, and an example value can be provided for each property.

We’re still missing something, though. The product resource we designed in the previous chapter didn’t only have literal properties (strings or numbers), as shown in figure 4.15.

04-16.png

Figure 4.15 A product with supplier description

Indeed, it also had a complex supplier property, which is mandatory. A supplier is defined by its reference and its name, which are both required. The following listing shows the updated JSON schema.

Listing 4.11 Describing a complex property with the JSON Schema

type: object
description: A product
required:
  - reference
  - name
  - price
  - supplier  
properties:
  reference:
    type: string
    description: Product's unique identifier
    example: ISBN-9781617295102
  name:
    type: string
    example: The Design of Web APIs
  price:
    type: number
    example: 44.99
  description:
    type: string
    example: A book about API design
  supplier:  
    type: object
    description: Product's supplier
    required:  
      - reference
      - name
    properties:  
      reference:
        type: string
        description: Supplier's unique identifier
        example: MANPUB
      name:
        type: string
        example: Manning Publications

①   The supplier is required.

②   The supplier object property

③   The supplier’s required properties

④   The supplier property descriptions

To add the supplier property to the product JSON schema, we add it to the properties list and set its type to object. We supply a description for the property and a list of its required properties (reference and name); then we describe those properties. For the reference property, we supply a type, description, and example. For the name property, we only set a type and example. Finally, we add the supplier property to the product’s required list.

As you can see, describing data using the JSON Schema is simple; you can describe any data structure using this format. Unfortunately, this book does not cover all of its features. To learn more, I recommend you read the Schema Object description in the OAS (https://github.com/OAI/OpenAPI-Specification/tree/master/versions), and then the JSON Schema specification (http://json-schema.org/specification.html). Now that you know how to describe data structures using the JSON Schema, let’s describe the search for products response.

4.3.3 Describing responses

When users search for products, they are supposed to get the products matching the provided free query (figure 4.16). The corresponding GET /products?free-query={free query} API request returns a 200 OK HTTP response whose body will contain an array of products.

04-17.png

Figure 4.16 Describing the search for products response

In an OAS document, the data returned by an operation in the body of an HTTP response is defined in its content property, as shown in listing 4.12. When describing the content of a response, you have to indicate the media type of the document contained in the response’s body. The media type is provided in the HTTP response. For now, as stated earlier, we’ll take it for granted that our API returns JSON documents, so we indicate application/json in the response. (We talk about this HTTP feature later in chapter 6.)

Listing 4.12 Describing the response’s data

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:
      get:
        summary: Search for products
        description: |
          Search using a free query (query parameter)
        parameters:
         [...]
        responses:
          "200":
            description: Products matching free query
            content:  
              application/json:  
                schema:  
                  [...]

①   Response body’s definition

②   Response body’s media type

③   Response body’s JSON schema

Once that is done, we can describe the schema of the returned JSON document using the JSON Schema, as shown in listing 4.13. The GET /products action returns an array of products. We already know how to describe a product using the JSON Schema, but how do we describe an array of products? As you can see in the next listing, an array is described using the array type. The items property contains the schema of the array’s elements. This array contains some products; you should recognize the product JSON Schema we created earlier.

Listing 4.13 Describing an array of products

responses:
  "200":
    description: Products matching free query
    content:
      application/json:  
        schema:  
          type: array  
          description: Array of products
          items:  
            type: object
            description: A product
            required:
              - reference
              - name
              - price
              - supplier
            properties:
              reference:
                description: Unique ID identifying a product
                type: string
              name:
                type: string
              price:
                description: Price in USD
                type: number
              description:
                type: string
              supplier:
                type: object
                description: Product's supplier
                required:
                  - reference
                  - name
                properties:
                  reference:
                    type: string
                  name:
                    type: string

①   Response body’s media type

②   Response body’s JSON schema

③   Response type is an array

④   Array’s items schema

The next listing contains an example of a JSON document returned in the response’s body corresponding to the JSON Schema.

Listing 4.14 Array of products JSON example

[
  {
    "reference": "123-456",
    "name": "a product",
    "price": 9.99,
    "supplier": {
      "reference": "S789",
      "name": "a supplier"
    }
  },
  {
    "reference": "234-567",
    "name": "another product",
    "price": 19.99,
    "supplier": {
      "reference": "S456",
      "name": "another supplier"
    }
  }
]

As you can see, describing the response’s data is pretty simple. And you know what? Describing body parameters is just as easy.

4.3.4 Describing body parameters

Let’s take a look at the add product action. To add a product to the catalog, the API user has to provide some product information in the body of their request, as illustrated in figure 4.17.

04-18.png

Figure 4.17 Describing the add product body parameter

Describing the body parameter of add product is done almost the same way as describing the response of search for products, as you can see in the following listing.

Listing 4.15 Describing an action’s body parameter

openapi: "3.0.0"
info:
  title: Shopping API
  version: "1.0"
paths:
  /products:
    description: The products catalog
    [...]
    post:
      summary: Add product
      description: Add product to catalog
      requestBody:  
        description: Product's information  
        application/json:  
          schema:  
            [...]
      responses:
        "200":
          description: Product added to catalog

①   Body parameter’s definition

②   Body parameter’s description

③   Body parameter’s media type

④   Body parameter’s schema

The body parameter of an HTTP request is described in its requestBody property. Like a response’s body, a body parameter has a media type (application/json), and its content is described with a JSON Schema. The complete description of this parameter is shown in the following listing.

Listing 4.16 Body parameter’s complete description

requestBody:
  description: Product's information
  content:
    application/json:
      schema:  
        required:
          - name
          - price
          - supplierReference
        properties:
          name:
            type: string
          price:
            type: number
          description:
            type: string
          supplierReference:
            type: string

①   Body parameter’s schema

The body parameter’s schema (or request body’s schema) is described like any other data in OAS using the JSON Schema. As previously designed, the mandatory information needed to add a product to the catalog is its name, price, and supplierReference. The description is optional. The following listing shows what the JSON document might look like.

Listing 4.17 Product information JSON example

{
  "name": "a product",
  "price": 9.99,
  "supplierReference": "S789"
}

See? It was as easy as anticipated. Why? Because describing the request’s and response’s body is done the same way. Providing a common way of doing different things is a basic principle of design. This approach can be used when creating anything, from a door to an API description format, to make it user-friendly. We will investigate this from the API design perspective later in chapter 6.

We only need to describe add product’s response to finish the catalog resource’s description. We have learned all we need to do that. We know how to describe an action’s response and its data. And, fortunately, search for products and add product return the same type of data, a product, so we already have the JSON Schema describing the response’s data. But if we do that as we have learned, we will duplicate the product JSON Schema in the OAS document. Let’s see how we can handle this more efficiently.

4.4 Describing an API efficiently with OAS

It’s always useful to dig into an API description format’s documentation to learn all its tips and tricks, just like you would do when learning a programming language. There are two basic things that you need to know when writing OAS documents:

  • How to reuse components such as JSON schemas, parameters, or responses
  • How to define path parameters efficiently

4.4.1 Reusing components

Both search for products and add product return product resources. It would be a pity to describe the same thing twice. Fortunately, OAS allows us to describe reusable components such as schemas, parameters, responses, and many others, and use them where needed using a reference (figure 4.18).

04-19.png

Figure 4.18 Reusable components in the OAS document

All we have to do to avoid describing a product JSON Schema twice is to declare it as a reusable schema. Reusable components are described in the components section that’s at the root of the OAS document. Within this section, reusable schemas are defined in schemas; each reusable schema’s name is defined as a property of schemas. This property contains the reusable component, a JSON Schema. In the following listing, product contains the product JSON Schema we created earlier.

Listing 4.18 Declaring a reusable schema

openapi: "3.0.0"
[...]
components:  
  schemas:  
    product:  
      type: object  
      description: A product
      required:
        - reference
        - name
        - price
        - supplier
      properties:
        reference:
          description: |
              Unique ID identifying
              a product
          type: string
        name:
          type: string
        price:
          description: Price in USD
          type: number
        description:
          type: string
        supplier:
          type: object
          description: Product's supplier
          required:
            - reference
            - name
          properties:
            reference:
              type: string
            name:
              type: string

①   Reusable components

②   Reusable schemas

③   Reusable schema’s name

④   JSON Schema

Now, instead of redefining the product JSON Schema, we can use a JSON reference to access this predefined schema when we need it. A JSON reference is a property whose name is $ref and whose content is a URL. This URL can point to any component inside the document, or even in other documents. Because we are only referencing local components, we use a local URL containing only a fragment describing the path to the needed element, as shown in figure 4.19. Here, product is located in schemas, which is located in components at the root of the document.

04-20.png

Figure 4.19 JSON references to local components

The following listing shows how we can use a reference for the POST /products response.

Listing 4.19 Using a predefined component with its reference

post:
  summary: Add product
  description: Add product to catalog
  [...]
  responses:
    "200":
      description: Product added to catalog
      content:
        application/json:
          schema:  
            $ref: "#/components/schemas/product"  

①   Response’s schema

②   Reference to predefined schema

When users add a product to the catalog, they get the created product in return. So when defining the response schema, instead of redescribing the product JSON Schema, we can just use a $ref property whose value is a reference to the predefined schema. We can do the same for search for product, which returns an array of products, as shown in the next listing.

Listing 4.20 Using a predefined component in an array with its reference

get:
  summary: Search for products
  description: |
    Search using a free query (query parameter)
  parameters:
    [...]
  responses:
    "200":
      description: Products matching free query
      content:
        application/json:
          schema:  
            type: array  
            items:  
              $ref: "#/components/schemas/product"  

①   Response’s schema

②   An array

③   Array’s items schema

④   Reference to predefined schema

The schema we have predefined only describes a product, not an array of products, so here we use the predefined schema to describe the array’s items schema. To do that, as before, we simply replace the schema with a reference ($ref) to the predefined schema. This means that we can combine inline and predefined definitions. Note that we can also use multiple predefined definitions when needed.

We’re now done with the catalog resource identified by the /products path and its two actions, get and post. These elements are fully and efficiently described thanks to the OAS and JSON Schema. There is one last thing to investigate with the OAS in order to be able to fully describe a basic REST API—how to describe a resource with a variable path.

4.4.2 Describing path parameters

The product resource, which can be deleted, updated, or replaced, is identified by a variable path (figure 4.20). Note that the get, update, and replace actions return the same product and also that the update and replace actions use the same parameter. Note also the /products/{productId} path contains a productId variable, which is called a path parameter.

04-21.png

Figure 4.20 The product resource and its actions

You already learned how to define parameters on the action level with GET /products?free-query={free query}, so let’s do this again with DELETE /products/{productId}. The following listing shows how we can define this in our OAS document for the delete action.

Listing 4.21 Deleting a product

paths:
  /products:
  [...]
  /products/{productId}:  
    description: A product
    delete:  
      summary: Delete a product
      parameters:  
        - name: productId  
          in: path  
          required: true  
          description: Product's reference
          schema:
            type: string

①   Product resource path with parameter

②   Delete product action

③   Delete product action’s parameters

④   Path parameter’s name

⑤   Parameter is located in path

⑥   Parameter is required

First we add a new /products/{productId} path to define the product resource. As you can see, the path parameter is identified with curly braces ({productId}) in paths. Then, we define this path parameter in the delete action’s parameters list. It is defined almost like any other parameter that goes in the parameters section: we need to set its name, location, and schema. The name must match the name inside the curly braces in the path, so we set it to productId. The location (in) is obviously path, and this parameter’s type, defined in its schema, is string.

We’re almost done, but there is one last thing we must not forget to do: because it is a path parameter, we also have to make this parameter mandatory by setting required to true. If we don’t do that, the parser throws an error.

That wasn’t so different from defining a query parameter. Now, what if we wanted to describe the product’s update and replace actions? We could describe the path parameter the same way, but that would mean duplicating this description in each new action. How can we do that more efficiently?

Earlier, we discovered the components section of the OAS document. This section allows us to define reusable components such as schemas and responses, and we can also describe reusable parameters here. The following listing illustrates how we do this.

Listing 4.22 Describing a reusable parameter

components:  
  parameters:  
    productId:  
      name: productId
      in: path
      required: true
      description: Product's reference
      schema:
        type: string

①   Reusable components

②   Reusable parameters

③   Reusable parameter’s name

To define a reusable parameter, we do exactly what we did for our reusable schema. In the components section at the root of the OAS document, each reusable parameter is defined as a property of parameters and identified by name. The productId property contains the definition of the productId path parameter as we have defined it for DELETE /products/{productId}.

Pretty simple, isn’t it? Like the JSON Schema, we use this predefined parameter with a JSON reference, as shown in the following listing.

Listing 4.23 Using a predefined parameter

components:
    parameters:
      productId:  
        [...]
paths:
  /products:
  [...]
  /products/{productId}:  
    delete:
      parameters:
        - $ref: #/components/parameters/productId  
      [...]
    put:
      parameters:
        - $ref: #/components/parameters/productId  
      [...]
    patch:
      parameters:
        - $ref: #/components/parameters/productId  
      [...]

①   Path parameter definition

②   Product resource’s path with parameter

③   Reference to predefined parameter

Instead of defining the same parameter three times, we simply use a $ref property pointing to the unique and reusable productId definition.

That’s much better! The productId parameter is defined once and used in three different places. But do you know what? We can do even better. Strictly speaking, the productId parameter is not an action’s parameter; it’s a resource’s parameter.

In an OAS document, parameters can be defined not only at the action level but also at the resource level, again in a parameters section. The structure of this section is exactly the same as at the action level. All parameters defined on the resource level are applied to all actions on the resource. Therefore, as the next listing demonstrates, to simplify our document even more, we can simply define the productId path parameter in the parameters section of the /products/{productId} path.

Listing 4.24 Resource-level parameters

components:
    parameters:
      productId:  
        [...]
paths:
  /products:
  [...]
  /products/{productId}:  
    parameters:  
      - $ref: #/components/parameters/productId  
    delete:  
      [...]
    put:  
      [...]
    patch:  
      [...]

①   Path parameter definition

②   Product resource’s path with parameter

③   Resource-level parameters

④   Reference to predefined parameter

⑤   No more path parameter definitions

Congratulations! With all that you have now discovered about OAS, you should be able to finish the descriptions of the product resource’s actions. But more importantly, you will now be able to create a formal description of any basic REST API using OAS and share it with all the people involved in your project. Don’t hesitate to dig into the OAS documentation (https://github.com/OAI/OpenAPI-Specification/tree/master/versions), use my OpenAPI Map (https://openapi-map.apihandyman.io), and experiment to discover other features.

This chapter concludes the first part of this book. You have acquired a basic set of API design skills, and you now know

  • What an API really is
  • How to identify its goals from the consumer’s perspective
  • How to transpose them into a programmable representation
  • How to formally describe this programmable representation using OAS

In the next part, we will improve on these skills so you can create APIs that anybody can use easily, without even thinking about it. In the next chapter, we dig into the API’s usability by investigating how to design straightforward APIs.

Summary

  • An API description format is a simple and structured way to describe and share a programming interface.
  • An API description document is a machine-readable document that can be used in numerous ways, including to generate API reference documentation.
  • You use an API description format only when designing the API’s programmable representation and data, and not before.
  • Always take advantage of an API description format’s documentation features. Explore the API description format’s documentation in depth so you can use it efficiently and, especially, to define reusable components where possible.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset