12
Documenting an API

This chapter covers

  • Reference documentation
  • User guides
  • Implementer specifications
  • Change logs

In previous chapters, you discovered that designing APIs requires more than just designing usable APIs doing the job. Indeed, we have to take care of the whole context surrounding the API when designing it. But this context goes beyond that of the API itself—its interface contract, its implementation, and how and by whom it is used. API designers have to participate in various aspects of API projects, and a very important one is documentation.

The best designs of even the simplest things need documentation. As shown in figure 12.1, everyday objects can come with various types of documentation to help users understand how to use them, as well as to help the people in charge of building those objects to actually build them.

12-01.png

Figure 12.1 Different types of documentation

Page 1 of the Alarm Clock User Manual shows an annotated figure of the alarm clock. Thanks to that, users know what the components of the user interface are and their roles, even if the device’s user-friendly design makes this fairly obvious. But this first page of documentation alone is not enough to operate the alarm clock. That’s why page 2 shows its various functions, such as how to set the alarm by using the Set Alarm and plus and minus buttons.

The user manual’s cover is also a kind of documentation: it advertises, “Now shows time in 24h mode” to indicate a new feature that was not present in the previous version. (This might not be of interest to new users, but it will be to people who owned the earlier version.) Without this documentation, most users, especially if they’ve owned one before, would be able to use the alarm clock because of its design. But some users, the absolute beginners, might struggle at first to guess how to operate it.

We’ve mentioned three different types of documentation that are user-oriented, but there is another kind of documentation. Although this user manual provides all the information needed to use the alarm clock, it’s not sufficient to actually build one. For example, the user manual does not state that the time on the LCD screen blinks when the Set Time button is pressed for five seconds, but it actually does that. The people in charge of building the alarm clock got the documentation from its designers in the form of implementation specifications describing such behavior. Without such documentation, there is little chance that a design will be implemented as expected by the designers. Without relevant documentation, all the effort that’s put into a design can be worthless.

As API designers, you will have to create or at least participate in the creation of such documentation for the APIs you are designing. The best-known API documentation is the reference documentation that describes the interface contract of the API. It lists the available goals and describes their inputs and outputs. This is what you have to describe when designing an API, which can be sufficient for very basic APIs if all use cases fulfilled by the API can be accomplished using a single goal. But if that’s not the case, only supplying reference documentation is like only providing the list of ingredients for a recipe without some indication of what to do with those ingredients—an edible result might be quite hard to achieve from that. That is why an API must also come with an operating manual describing various use cases and how to achieve them.

Additionally, as with any software, when modifying an API, even if no breaking changes are introduced, it is wise to provide a change log indicating the features that have been changed or added. As the designer who knows what changes you have made, it is up to you to list those changes. And last but not least, providing a description of the API might not be enough to allow someone to implement it. You might also have to provide additional specifications to the people in charge of the API’s implementation in order to ensure that the result behaves as expected.

Your involvement in each of these kinds of documentation depends on the type of documentation, the size of your company or team, and the type of API (private, partner, or public). In a big company and/or team, technical writers might be available to produce high-quality documentation; you only have to provide support and raw inputs. Relying on technical writers is especially important for consumer-facing documentation of public APIs; writing usable and user-friendly documentation requires experts in order to ensure a top-quality developer experience. For private APIs, the expectations might be lower, and the documentation might be less eye-catching, but the developer experience should still be a major concern. Documentation for internal developers must at least be readable and exhaustive. Exhaustively documenting APIs has a nice side effect—it is testing the design. If you are unable to document how to use the API or how to implement it, it can be a sign of improper design.

In this chapter, we will discover what reference documentation, operating manuals, implementation specifications, and change logs might contain, and how we as API designers can contribute to them by taking advantage of our work during the design of the API. What you learn to do here might be sufficient for wholly documenting private APIs; for consumer-facing partner or public APIs, your work will be a good input for more experienced technical writers.

12.1 Creating reference documentation

API reference documentation like that shown in figure 12.2 is like the annotated alarm clock schema in figure 12.1: it lists and describes each available component of the interface.

12-02.png

Figure 12.2 Reference documentation generated from an OpenAPI Specification file using the ReDoc open source tool

For an API the components are, at minimum, the available goals and their inputs and outputs for both success and error cases. (In the case of the alarm clock, these components were its buttons and LCD screen.) Documentation of the API should also contain a simple description of it and provide information about security. All this information could be written in any format, from a simple text file to a wiki page. Some people even dare to use spreadsheets (please don’t!). Any custom format could do the job, but you saw in section 6.1.3 that using standards is better when designing an API, and this is also the case when creating API documentation.

You learned about some of these standards in chapter 4. Indeed, API description formats such as the OpenAPI Specification are the perfect companion when you want to create API reference documentation. These formats are made to describe what is needed in such documentation; and, as seen in section 4.1.2, they can be easily stored, versioned, and most importantly, used to generate a human-friendly representation. The tool used to generate the reference documentation based on the OpenAPI Specification file shown in figure 12.2 is called ReDoc.

ReDoc is one tool among many. Many API tools, especially API developer portals, natively understand the OpenAPI Specification (and others) and so can generate such renderings without you having to code anything. But format alone isn’t enough. If the description of your API does not contain all the needed information, the generated documentation will be incomplete, even if these tools are able to guess a few things by themselves.

What follows is illustrated with the OpenAPI Specification and ReDoc, but you can use other API description formats and rendering tools. The important things to remember here are what information is needed in the reference documentation and what you should expect from API description formats and renderers. We will start by documenting the data model, then the goals (paths and HTTP methods, input, outputs). After that, we will deal with security; and finally, we will see how to add some useful information about the API itself.

12.1.1 Documenting data models

Figures 12.3 and 12.4 show a detailed data model and an example of the request body parameter, respectively, needed to create a money transfer.

12-03.png

Figure 12.3 Data model reference documentation

12-04.png

Figure 12.4 Data model reference example

The reference documentation in figure 12.3 shows us that this data model is composed of source, destination, amount, date, occurrences, and frequency properties. They are all of type string except for amount, which is a number, and occurrences, which is an integer; additionally, source, destination, and amount are mandatory (required) properties. This is the most fundamental information needed in reference documentation, as it is when describing an API in the design phase (see section 3.3.1).

But this reference documentation does more than just provide the minimum: it also provides useful functional and technical descriptions and examples. The documentation gives detailed information about each property’s format and value. The source and destination properties must be 15 characters long and contain only digits, according to the /^d{15}$/ regular expression. The amount is an exclusively positive number, and the value of occurrences must be between 2 and 100. The possible values of the frequency property are "WEEKLY", "MONTHLY", "QUARTERLY", and "YEARLY". The example shown in figure 12.4 helps us to visualize what each property looks like.

Furthermore, the date, occurrences, and frequency descriptions give useful information about how these properties can be used for creating delayed or recurring transfers. Note that amount has no description because its name and the context make it clear that it specifies the amount of money to transfer from source to destination.

All this information comes from a JSON Schema defined in the underlying OpenAPI Specification file (two excerpts are shown in listings 12.1 and 12.2). You already learned in section 4.3.2 how to define a property’s name, type, and description; how to state if a property is mandatory; and how to provide an example.

Listing 12.1 A very complete description of a property with an example

components:
  schemas:
    TransferRequest:
      description: A money transfer request
      required:
        - source
        - destination
        - amount
      properties:
        source:
          type: string
          description: Source account number
          minLength: 15  
          maxLength: 15  
          pattern: ^d{15}$  
          example: "000534115776675"  

①   Minimum length for source property

②   Maximum length for source property

③   Format for source property (a regular expression)

④   An example value

The source property’s length is 15 because its minLength and maxLength values are both 15. Its exact format (a string composed of 15 digits) is defined by pattern, which contains the ^d{15}$ regular expression (000534115776675, for example).

ReDoc is actually able to guess the property’s length based on the regular expression, but not all tools take advantage of this information. The source’s value shown in figure 12.4 comes from the example provided in listing 12.1, but documentation tools can also guess example values based on descriptions, as show in the following listing.

Listing 12.2 Documentation tools rely on descriptions to generate examples

        [...]
        date:
          type: string
          format: date  
          description: |
            Execution date for a delayed transfer
            or first execution date for a recurring one
        [...]
        frequency:
          type: string
          description: Frequency of recurring transfer's execution
          enum:  
              - WEEKLY
              - MONTHLY
              - QUARTERLY
              - YEARLY

①   The date property is a string using a date format (YYYY-MM-DD); today’s date is used in the documentation.

②   Possible values for frequency; a random value is shown in the documentation.

The date property uses the date format, which means the value will be a YYYY-MM-DD ISO 8601 full date: 2019-03-23, for example. The frequency property’s possible values ("WEEKLY", "MONTHLY", "QUARTERLY", and "YEARLY") are defined in the enum list. There are no examples set for the date and frequency properties, but the reference documentation still provides some: these have been generated based on the JSON Schema.

For the date property, being a date, ReDoc simply uses today’s date (2019-05-01 in figure 12.4), and for frequency, it uses a random value from the enum (WEEKLY in figure 12.4). I’ll let you find out for yourself how the other properties have been described.

As you can see, basic but valuable reference documentation for an API’s data model can be created by just reusing the work done during the API’s design. Simply listing properties, their types, and if they are mandatory, without even providing examples or extensive descriptions, could be sufficient for simple data models. Consumers might not thank you for providing such basic reference documentation, but be sure that they will curse you if you do not provide any at all.

If you add examples and, more importantly, detailed and relevant descriptions, including both machine-readable (like formats or possible values) and human-readable ones, the resulting reference documentation will be of help to anyone using it on either the consumer’s or the provider’s side. There is no need for “Captain Obvious” descriptions like “amount: the transfer’s amount” or, even worse, “amount: the amount.”

If stating a property’s name, type and context is sufficient for a user to understand its meaning; there is no need to add a description. If the API description format and rendering tool you use support formatted human-readable descriptions (using Markdown, for example), do not hesitate to use this feature to make long descriptions easier to read.

12.1.2 Documenting goals

A goal’s reference documentation describes the goal’s purpose, what is needed to use it, what kind of feedback consumers get in the event of success or failure, and if it is part of a group of goals. For example, figure 12.5 gives an overview of the transfer money goal.

12-05.png

Figure 12.5 An overview of the transfer money goal

As you can see, the goal is represented by a POST /transfers request. According to the description, this goal allows users to create immediate, delayed, or recurring money transfers. Each type of transfer is explained from a functional point of view, and there is also information about the use of the various properties that can be shared by all types or those specific to one. All types of transfers need an amount, source account, and destination account. We can also see that a delayed transfer is executed on a given future date, and that a recurring transfer requires a date as well as a number of occurrences and a frequency.

All the information provided in this description could be inferred by analyzing the request body data model previously shown in figure 12.3, but it’s better to provide a more human-readable description instead of simply saying, “Create a money transfer” or “Create an immediate, delayed, or recurring money transfer.” Such a description makes documentation very user-friendly, but this can be even better. Look at the request samples shown in figure 12.6.

12-06.png

Figure 12.6 Multiple goal input examples

There are Immediate transfer, Delayed transfer, and Recurring transfer tabs, the latter being selected. This reference documentation provides an example of a request for each type of money transfer. This is a must-have! Users don’t have to think to get almost-ready-to-use examples for various use cases; they just have to tweak the provided values. That’s for the inputs, but an API reference documentation must also provide detailed information about possible responses, as shown in figure 12.7.

12-07.png

Figure 12.7 Goal outputs

For each response, there is a human-readable description and the response body description (using the same format as the request body). The reference documentation shows that in the case of success, a 201 or 202 HTTP status can be returned. A 201 Created HTTP status is returned when a transfer is accepted, and no date property has been provided (for immediate or recurring transfers with the first occurrence executed immediately). A 202 Accepted HTTP status is returned when a transfer is accepted, and a date property has been provided (for delayed or recurring transfers with a delayed first occurrence). Note that the response body schemas are not visible; this is to keep the screen capture small.

In the case of an error in the transfer request, a 400 Bad Request HTTP status is returned. Its response body is partially shown (again to keep the screen capture small). The interesting thing here, though, is the description, which provides details about all the possible errors and how they are represented.

As for the input, multiple examples can be provided for each response, as shown in figure 12.8. Here, 202 is selected. Delayed and recurring transfer samples are available, and the latter is selected.

12-08.png

Figure 12.8 Multiple goal output examples

In section 7.1.3, you learned that if an API provides multiple goals, their organization is important. Figure 12.9 shows how this can be managed in reference documentation.

12-09.tif

Figure 12.9 Goal organization

Figure 12.9 shows the reference documentation’s menu (left column in figure 12.2). Goals are organized in various categories, with the Transfers category comprising all transfer-related goals (“Create a money transfer” or “Cancel a money transfer,” for example). As with the data model reference documentation, everything that is shown comes from an underlying OpenAPI Specification file. The following listing puts into practice what you learned in section 4.2.3 to describe a goal.

Listing 12.3 The transfer money goal’s basic reference documentation

[...]
paths:
  /transfers:  
    post:  
      summary: Creates a money transfer  
      requestBody:  
        content:
          "application/json":
            schema:
              $ref: "#/components/schemas/TransferRequest"
      responses:  
        201:
          description: Immediate or recurring transfer executed
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/TransferResponse"
        [...]
        400:
          description: Transfer rejected  
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/Error"  
[...]

①   The resource path

②   The HTTP method used on the resource.

③   A short description of the (resource, method) couple

④   The goal’s input

⑤   The goal’s possible outputs

⑥   A brief description of the feedback

⑦   A reference to the feedback data model

The goal is described with a brief summary, and all the possible responses and their data models are listed without much detail. The various reference documentation screenshots you have seen provided more information. That’s because the OpenAPI Specification file used obviously contains more information. The next listing shows the underlying detailed description of the transfer money goal.

Listing 12.4 The transfer money goal overview’s OpenAPI Specification file

paths:
  /transfers:
    post:
      summary: Transfers money
      description: |  
        This operation allows one to transfer an `amount` of money from a
        `source` account to a `destination` account.
        There are three different types of money transfer:
          - Immediate, they are executed as soon as the request is received
          - Delayed, they are executed upon a given future `date`
          - Recurring, they are executed a given `occurrences` number of
          times at a given `frequency`, the first occurrence being executed
          immediately or at a given `date`

①   A multiline description in Markdown

Here, after the summary (short description), a complete multiline description taking advantage of the Markdown format is provided. The next listing shows how to provide multiple examples of the transfer money goal’s request body.

Listing 12.5 Multiple examples of the transfer money goal’s request body

[...]
paths:
  /transfers:
    post:
[...]
      requestBody:
        content:
          "application/json":
            schema:
              $ref: "#/components/schemas/TransferRequest"
            examples:  
              immediate:
                [...]
              delayed:
                [...]
              recurring:  
                summary: Recurring transfer
                description: |
                  The money transfer is executed at a
                  given date recurringly
                value:
                  source": "000534115776675"
                  destination: "000567689879878"
                  amount: 456.2
                  date: "2019-03-19"
                  occurrences: 1
                  frequency: "MONTHLY"
              [...]
[...]

①   Multiple examples for the request body

②   An example comes with a summary, description, and value

The Request Samples pane in figure 12.6 showed three tabs: Immediate transfer, Delayed transfer, and Recurring transfer. Their content came from the examples property after the schema property. Each example has a summary (tab name), description, and value (tab value). Unfortunately the description of each example is not shown in ReDoc.

Providing multiple examples can be done for other types of input parameters too (query or header parameters, for example), as well as response bodies. Speaking of responses, remember that you learned in section 5.2.2 that we should list all possible errors. These obviously must be documented beyond just listing the possible 4XX or 5XX status codes in the reference documentation. The next listing shows how to take advantage of a complete multiline-formatted description to do so.

Listing 12.6 A detailed error description

[...]
paths:
  /transfers:
    post:
      [...]
      responses
        [...]
        400:
          description: |
            The transfer is rejected due to an error in the request
            properties or an insufficient balance. Each error provides the
            property `source` of the error along with a human-readable
            `message` and its `type`:
 
            - MANDATORY_PROPERTY: The property indicated in `source`
              is missing
            - INVALID_FORMAT: The format of the property indicated in
              `source` is invalid
            - INVALID_VALUE: The value of the property indicated in
              `source` is invalid
            - INSUFFICIENT_BALANCE: The `amount` property is higher than
              the `source` account balance
          [...]
[...]

For the goal groups, there’s nothing new. You learned in section 7.1.3 how to define them, as shown in the next listing.

Listing 12.7 Describing tags

[...]
tags:  
  - name: Transfers
    description: Everything you need to transfer money
 
paths:
  /transfers:
    post:
      summary: Create a money transfer 
      tags:  
        - Transfers
[...]

①   Optional tags definition and description

②   Tags to which the goal belongs

So good reference documentation for an API goal, like the data model, requires a relevant human-readable description and relevant examples (the more, the better). A good description of the possible errors is especially important. Besides providing multiple examples, this is basically what you have to describe when you design the API, so this kind of reference documentation is easy to create once the API has been designed.

12.1.3 Documenting security

Reference documentation must also contain information about security, as shown in figures 12.10 and 12.11.

12-10.png

Figure 12.10 How the Banking API is secured and what the available scopes are

12-11.png

Figure 12.11 Which scopes are needed to create a money transfer

The Authentication section in figure 12.10 shows that the Banking API is secured using the OAuth 2.0 implicit flow, and the (visible) available scopes are transfer:create, transfer:read, transfer:delete, transfer:admin, and beneficiary:create. On the Create a money transfer screen in figure 12.11, the Authorizations section states that consumers must have the transfer:create or transfer:admin scope to be authorized to use the transfer money goal. There’s nothing new here; you saw how to describe API security using the OpenAPI Specification in section 8.2.4. The following listing shows an excerpt of the underlying OpenAPI Specification file.

Listing 12.8 Defining API security and attaching scopes to a goal

[...]
components:
  securitySchemes:  
    BankingAPIScopes:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: "https://auth.bankingcompany.com/authorize"
          scopes:
            "transfer:create": Create transfers
            "transfer:read": List transfers
            [...]
[...]
paths:
  /transfers:
    post:
      summary: Create a money transfer
      security:  
        - BankingAPIScopes:
          - "transfer:create"
          - "transfer:admin"
[...]

①   Security and scope definitions

②   Scopes needed to use the goal

ReDoc automatically adds an Authentication menu showing everything defined in the components.securitySchemes section of the OpenAPI Specification file. If security is defined on a goal, ReDoc also shows an AUTHORIZATIONS: entry.

Again, because you have to define how the API is secured when designing it, not much effort is needed to provide basic reference documentation describing how the API is secured, the available scopes, and which scopes are needed to use each goal.

12.1.4 Providing an overview of the API

Last but not least is the API-level reference documentation. Figure 12.12 shows how ReDoc uses the info section of an OpenAPI Specification file (shown in listing 12.9) for this.

12-12.png

Figure 12.12 An API’s short description and contact information in the reference documentation

Listing 12.9 The info section of the underlying OpenAPI Specification file

info:
  title: Banking API
  version: "1.0.0"
  description: |
    The Banking API provides access to the
    [Banking Company](http://www.bankingcompany.com) services, which
    include bank account information, beneficiaries, and money transfer
    management.
  contact:
    name: The Banking API team
    email: [email protected]
    url: developer.bankingcompany.com

You learned how to define an API’s name (title) and version when describing an API using the OpenAPI Specification in section 4.2.1. Here we’ve added a description and some contact information. The description provides an overview of the Banking API and takes advantage of the Markdown format to provide a link to the Banking Company’s website. The contact information consists of the name of the team managing the API, their email address, and the API’s developer website’s url.

Providing a brief description of the API is mandatory in an API’s reference documentation; it helps consumers understand what can be done using the API if its name alone is not enough. The contact information is optional, but you should always provide users a way of getting more information or help.

12.1.5 Generating documentation from the implementation: pros and cons

Documentation can be generated from the implementation code alone or code plus annotations; that was the original intent of the Swagger framework (see section 4.1.1). Such a strategy has the advantage of keeping implementation and documentation synchronized, but know that it has a few drawbacks:

  • I do not recommend only relying on a pure generation based on code, as the resulting documentation will be far from complete.
  • Existing annotation frameworks, at least the one I have been working with, do not allow the same flexibility as you get when working directly with an API description format (for example, providing examples adapted to various contexts when using generic data structures shared across the API is impossible).
  • Including documentation in the code implies that you will actually modify the code to fix the documentation. That could be a problem depending on who works on what (documentation versus code) and your organization and your confidence level when modifying the code (yes, not all organizations in the world are able to push all applications automatically into production on every single commit without fearing anything).
  • In the early stages, code has to actually be written to generate documentation. If you remember the beginning of this book, you know how this can expose the provider’s perspective.

Obviously, keeping the documentation outside the code can also have some drawbacks; the major one, being able to keep documentation and code synchronized. Know that there is no good or bad strategy regarding this matter; you have to choose one that works for you and your organization.

So, whatever the means, as an API designer, you can create good API reference documentation without much effort, especially if you take a little time to provide detailed machine- and human-readable descriptions and (multiple) examples. Like design, writing good documentation requires practice, and reference documentation is a good place to start. Remember that seasoned technical writers might be needed if this documentation is for a partner or public API.

12.2 Creating a user guide

Complete reference documentation listings and describing every component of an API is a must-have, but as mentioned earlier, when following a recipe, if you only have the ingredients list, you can struggle to achieve something edible. An API user guide, like the example in figure 12.13, is meant to explain how to actually use the API. It describes how to use the API as a whole, as well as its principles and how to get access to it (registration and getting access tokens). When you’re providing public APIs, this documentation can be fairly dynamic.

12-13.png

Figure 12.13 An API user guide

12.2.1 Documenting use cases

In figure 12.13, there is a Use cases menu in the left pane. It contains two items: Transferring Money to an Account or Preexisting Beneficiary and Canceling a Delayed or Recurring Transfer. The first option is selected. Each item is obviously intended to describe a use case that exposes how various goals of the API can be combined in order to achieve something.

In the right pane, showing the transfer money use case, there is some text explaining what a money transfer is, the concept of source and destination accounts, and which goals should be used to select appropriate values for these properties. There is also a diagram below the text showing how to proceed:

  1. Call the list sources goal.
  2. Select a source account in the resulting list.
  3. Call the list source’s destinations goal for the selected source.
  4. Select a destination account in the returned list.
  5. Decide on an amount.
  6. Call the transfer money goal with the selected source and destination and the amount.

Does that not seem familiar? This looks like the information you learned to gather in section 2.3 and put in an API goals canvas like the one shown in figure 12.14.

12-14.png

Figure 12.14 An excerpt of the API goals canvas made while designing the Banking API.

An API goals canvas describes use cases that can be achieved using various goals. This means that, again, the work done during the design of the API can be reused to document the API. The API goals canvas can be reused more or less as is for private APIs and as a raw input for partner or public APIs.

Describing use cases in an API user guide can be done in many different ways, using many different tools. For basic user guides, all you need is to be able to write formatted text and, possibly, add some diagrams or images. You can use a simple content management system (CMS), a developer portal (which usually include CMS features), or even build your own custom website. For illustration purposes here, I’ll continue to use ReDoc and the OpenAPI Specification file shown in the following listing.

Listing 12.10 How ReDoc takes advantage of the OpenAPI Specification file

[...]
info:
  title: Banking API
  version: "1.0.0"
  description: |  
    The Banking API provides access to the
    [Banking Company](http://www.bankingcompany.com) services, which
    include bank account information, beneficiaries, and money transfer
    management.
 
    # Use cases  
 
    ## Transferring money to an account
             or preexisting beneficiary  
 
    The _transfer money_ operation allows one to transfer an `amount` of
    money from a `source` account to a `destination` account or
    beneficiary.
    In order to use an appropriate `source` and `destination`, we recommend
    to use _list sources_ and _list source's destinations_ as shown in the
    figure below (instead of using _list accounts_ and
    _list beneficiaries_).
  
    ![Diagram](http://developer.bankingcompany.com/diagrams/transfer.svg)
 
    ## Canceling a delayed or recurring
                        money transfer  
 
    - List money transfers: To list existing money transfers and select the
      one to delete
    - Cancel a money transfer: To cancel the selected money transfer
[...]

①   The description of the API

②   A Markdown level 1 header

③   A Markdown level 2 header

④   This description includes an image.

⑤   A simple text-based description

I’ve added some text and included an image in the info.description section of the OpenAPI Specification file, taking advantage of the Markdown format. If the description contains level 1 and 2 headers, ReDoc automatically adds them to the left pane as menu items and submenus. If it contains an image (![Text](URL)), it is shown as long as the URL can be accessed by the browser in which the ReDoc documentation runs. Diagrams can definitely be of great help for consumers (as the saying goes, a picture is worth a thousand words), but you can also simply use text, as shown in the second use case (Canceling a delayed or recurring money transfer).

Including the user guide in an API description file can be complex to manage in the long run for big APIs with many use cases. The people in charge of maintaining the API description might not be the ones maintaining the user guide, and the two forms of documentation might not have the same lifecycle. If you use a completely different tool or system for the API user guide, don’t forget to use info.contact.url or add a link in the description pointing to where it is located.

12.2.2 Documenting security

Another topic that is important in an API user guide is what consumers have to do to actually make an API call. It would be a pity if consumers knew which API calls to make to trigger a money transfer, but couldn’t actually make those calls because they didn’t know how to get a security token. An API user guide must include advice about how to register as a developer, register a consumer app, and get tokens using the available OAuth flows or whatever other security system/framework is in use (see section 8.1).

Unfortunately, there is nothing much to reuse here from the design phase (the scopes list is not of great help); but fortunately, such documentation should be almost the same for all of your APIs, and there are literally thousands of existing resources that can be used as inspiration to explain how to get an access token using an OAuth flow once the consumer application is registered.

12.2.3 Providing an overview of common behaviors and principles

An API user guide can also contain information about all of the API’s common behaviors and principles; security is but one of these. Such documentation can explain how errors are handled (see section 5.2.3), the available data formats and languages supported, or how pagination is handled (see section 6.2). In sum, you should include everything that is common to your API goals and worth mentioning to consumers in order to facilitate the use of the API.

12.2.4 Thinking beyond static documentation

This topic is totally out of the scope of this book, but know that static documentation is not the only option. The most-praised (public) APIs usually provide awesome developer portals, including high-quality reference documentation and user guides—and all of this is constructed in a totally dynamic way.

For example, while browsing the reference documentation of such an API, you might see a Try It! button that lets you actually call the API using a prefilled request, with the developer portal handling all the security business under the hood. Similarly, some user guides allow you to test use cases, step-by-step, inside the developer portal.1 

1 Look at Twilio (https://www.twilio.com) and Stripe (https://stripe.com) to discover first-class documentation.

12.3 Providing adequate information to implementers

We’ve explored how to document an API for consumers, but before any of them start to actually consume the API, it has to be implemented. And what do the people in charge of the implementation need? They obviously need a detailed description of the interface contract and, possibly, how it is supposed to be used; but that’s not enough. They also need a description of what happens under the hood.

When the Banking API was implemented, the resulting API was not exactly the expected one. For example, the account balances were implemented as objects containing a value as a number and a currency as a string. Unfortunately, for a $123.45 balance, the value was 12345 (the balance in cents, as it is stored in the banking system) and the currency was "C123" (the internal $ currency code). Also, the returned balances were not the real-time ones, but the daily ones, updated once a day at midnight.

There were also problems with error handling. For example, when a money transfer request was missing both a destination account and an amount, the error feedback only indicated the first problem. And if the account’s balance was insufficient, the error type was the internal error code "0002". More frightening, if a consumer using a security token linked to a given customer requested an existing account with a GET /accounts/{accountNumber} request, and it did not belong to the consumer, the consumer got a 200 OK response with the account instead of a 404 Not Found. It was fortunate that someone had the idea of conducting tests that uncovered these issues (we will talk more about that in section 13.3.5).

After some investigation, the project team realized that the developers in charge of the implementation did not get enough information about the API contract, how it should map to the underlying system, and what the expected security controls were. More importantly, they lacked knowledge of API security in general. All of the problems were solved by enhancing the API description (the OpenAPI Specification file) as shown in figure 12.15 and listing 12.11, and by providing training and guidance.

12-15.png

Figure 12.15 API description with enhanced description and implementation information

Listing 12.11 Adding custom properties into an OpenAPI Specification file

[...]
properties:
  value:
    description: |
      Balance's value using the number of decimal places as defined
      by ISO 4217
    externalDocs:  
      description: Decimal places table
      url: https://www.currency-iso.org/en/home/tables/table-a1.html
    type: number
    x-implementation:  
      description: The real time balance (not the daily one!)
      source:
        system: Core Banking
        location: ZBAL0.RTBAL
  currency:
    description: An ISO 4217 code
    externalDocs:  
      url: https://www.iso.org/iso-4217-currency-codes.html
    type: string
    example: USD
    x-implementation:
      source:
        system: Core Banking
        location: ZBAL0.RTCUR
[...]

①   A link to some documentation

②   Custom data ignored by standard parsers

③   A link without a description

The data format problems were due to an incomplete description of the amount’s data model in the API description file. If the description for the amount’s value property had stated that the number of decimal places was the one defined by the ISO 4217 standard, that would have given a hint about its format. The same goes for the currency description, which should have stated that this value was an ISO 4217 three-letter code. For both properties, links to external documentation (externalDocs) about ISO 4217 have been added. Note that the first link to the decimal places table has a description that ReDoc uses to render the link.

For the balance information, because the Banking API is used by non-expert consumers, there was no need to state in its description that it is the real-time balance. This means that it must be stated in another place only visible on the provider’s side. The team (including the designer and developers) chose to define it in the OpenAPI Specification file using a custom (or vendor) extension x-implementation, containing a detailed description about the source of the data (the system and location).

Standard OpenAPI Specification parsers ignore all properties starting with x-, so the x-implementation property’s name and format are totally custom and were defined by the team. Tools such as ReDoc might render the data contained in such a property, but as you can see in figure 12.15, it is rendered as a JSON object. To get a more human-friendly rendering, you have to customize the tool. The OpenAPI Specification file also has been enhanced to contain information about security controls, as shown in the next listing.

Listing 12.12 Security controls information on the get account goal

[...]
paths:
  /accounts/{id}:
    get:
      summary: Get an account
      x-implementation:
        security:
          description: |
            Only accounts belonging to user referenced in security data;
            return a 404 if this is not the case
          source:
            system: security
            location: jwt.sub
          fail: 404
[...]

The x-implementation property contains a description of the security controls that have to be performed, the security data to use, and which HTTP status to return in case of failure. Besides this detailed implementation documentation, developers have been trained to understand API security concerns, and they also have been provided with guidelines (we talk more about this in section 12.2). The error problems were fixed by adding more detailed information in the consumer-oriented documentation (as seen in section 12.1.1) and by also explaining error-handling principles (as seen in section 12.1.2).

So, the first step in providing relevant documentation to the API implementers is providing detailed consumer-facing documentation (a reference documentation and user guide). This documentation does not have to be flashy and eye-catching, but it must be exhaustive. But this is not enough.

The implementers also need provider-facing documentation about what actually happens under the hood. They need information about the data mapping (which system each piece of data comes from), error mapping (how to transform internal errors into consumer-facing errors), security data and controls, and expected behaviors based on internal business/technical rules.

As you’ve seen, this information can be documented within the API description file, but this is only one option. Choosing how to provide this information is up to you, as the API designer, and the people you are working with. Besides the actual API documentation, the implementers' documentation also consists of general guidance and training (see section 12.2).

12.4 Documenting evolutions and retirement

You learned in section 9.1 how to handle API design and evolutions in order to limit the introduction of breaking changes. But changes, breaking or not, will inevitably happen, and they have to be documented.

Such documentation is useful for consumers to keep them aware of new features and let them know if they need to modify their code in the event that elements are deprecated (or worse, retired). It can be also useful for all the other people involved in the project, providing them with an overview of upcoming changes in the next version.

And who is the best person to document or at least list all these changes? You! As the person who designed the changes, you know best what you have done. Figure 12.16 shows a very basic change log describing the latest modifications made to the Banking API.

12-16.png

Figure 12.16 A simple change log listing modifications made in each version

A change log should state which elements (data model properties, parameters, responses, security scopes) have been added, modified, deprecated, or retired. Here, we simply take advantage of the info.description section of an OpenAPI Specification file to add a Change Log level 1 header containing level 2 sections for each version, as we did for the use cases. API description formats do not, at least at the time of this book’s writing, propose ways of describing such a change log, but they can at least provide ways of indicating deprecated elements, as shown in figure 12.17.

12-17.png

Figure 12.17 Indicating deprecated elements using the OpenAPI Specification file

The List money transfers for admins goal in the lefthand menu is struck through, and the t query parameter of the List money transfers goal, shown in the right pane, is indicated as deprecated; they both have a deprecated flag set to true in the OpenAPI Specification file, as shown in listings 12.13 and 12.14.

Listing 12.13 Deprecating the List money transfers for admins goal

  /admin-transfers:
    get:
      summary: List money transfers for admins
      tags:
        - Transfers
      description: Redirects to GET /transfers
      deprecated: true  
    responses:
      "200":
          description: Transfers list
          content:
            "application/json":
              schema:
                $ref: "#/components/schemas/TransferList"

①   Goal deprecation flag

Listing 12.14 Deprecating the t query parameter

  /transfers:
    get:
      summary: List money transfers
      parameters:
        - name: t
          in: query
          description: replaced by type
          deprecated: true  
          schema:
            type: string
        - name: type
          in: query
          description: transfer type
          schema:
            type: string

①   Parameter deprecation flag

According to the OpenAPI Specification, the deprecated flag can be used on parameters, goals, and properties in data models. Here, the descriptions of the deprecated elements indicate what to use as replacements. These descriptions could also provide an indication about when the deprecated elements will be retired (if they are to be). You can also use some x- custom properties to add extra structured information about the deprecations instead of using text in the descriptions.

Documenting deprecated elements can sometimes be done in a dynamic way by providing metadata in API responses. For example, the Sunset header defined by RFC 8594 (https://tools.ietf.org/html/rfc8594) allows a server to communicate the fact that a resource is expected to become unresponsive at a specific point in time. If the Banking Company introduces a version 2 of the Banking API on August 4, 2019, and lets all consumers take six months to update their code, any call made on any resource of version 1 (like a GET /v1/accounts request) can return the response shown in the following listing, stating that the resource will not be available after February 4, 2020.

Listing 12.15 A response with a Sunset header

200 OK
Sunset: Tue, 4 Feb 2020 23:59:59 GMT
 
{ "items": [ ... ] }

In the next and final chapter, we will expand even more the context around API design in order to be able to work as API designers on a long-term basis on many APIs, even ones we do not actually design.

Summary

  • API designers must participate in the creation of different types of API documentation.
  • A detailed reference documentation is a good thing, but it is not enough. We must also create a user’s guide.
  • User guides must provide all needed information to use the API as a whole, including how to obtain credentials and tokens.
  • Leveraging an API description language such as the OpenAPI Specification can be of great help when creating documentation.
  • It is important to keep track of modifications in order to inform users of changes.
  • Creating documentation helps to test the API design.
..................Content has been hidden....................

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