API Design Governance – Style Guides in Apiary

Much has been written on the design of ideal REST APIs, from Roy Fielding’s original description of HATEOAS interfaces, to much more practical approaches mirroring APIs rolled out by large technology companies. When working alone, I have a lot of freedom in how I design and build my APIs, and I always strive to design APIs which I would love to consume, based upon a number of undocumented, but strongly-held design intuitions. Collections are plurals; sub-objects are used sparingly, and mostly for practical considerations like payload size; HTTP status codes are used appropriately for particular types of errors and responses; etc.

When I work as part of a larger team, I often find that we end up building interfaces with slight inconsistencies, even if the design of them was based upon some documented high-level design principles. These inconsistencies impact the productivity of both internal and external developers which have to use these APIs, as they have to carefully parse the documentation to develop around the ‘quirks’ of the individual APIs.

Ironing out these inconsistencies can be achieved in a couple of ways, adopting a waterfall-style development model, in which each team is required to submit their detailed design specifications to an architecture council for review and sign-off; or putting a system in place which checks new API designs for consistency, and provides real-time feedback to API designers as they sketch out the interface. Oddly enough, the approach that I am going to discuss in this blog post is not the former; instead we are going to explore the Style Guide capabilities offered by Apiary.io, which allows us to develop rules governing API styles, which are assessed in real-time during API design.

API Style Guides are a feature of Apiary Cloud Service Professional, which is intended for large development organisations, with many teams developing APIs separately and alongside one another. API Styles are managed centrally, and are intended to enforce consistency across the entire suite of APIs designed using Apiary by the organisation.

The Styles section of Apiary can be accessed by selecting the ‘Styles’ button from the top Nav Bar when managing the Apiary team, which then allows you to run reports on the current APIs from the Dashboard, edit the Rules from the Assertions tab, or live-test and edit your rules from the Playground tab. Most of this post will focus on the Assertions tab, as that is where the implementation of rules are defined.

An API Style Guide is split into two parts – a list of descriptive ‘Rules’ and implemented ‘Definitions’ which enforce them. Definitions are functions written in Javascript, while a rules object, which is expressed in JSON, maps those functions to the sections of the API to which they are relevant, as well as providing a plain-text description of what the rule is, and why it is important.

A simple, though perhaps a little silly rule definition could look like this:

{
    "ruleName": "Ensure URIs are not in all Caps",
    "functionName": "validateURINotUppercase",
    "target": "Resource_URI_Template",
    "intent": "URIs shouldn’t be in capital letters, we don’t want developers to think we are yelling at them."
}

This is a rule designed to make sure we don’t have all capital letters in our URIs. It has a target of the API specification’s URI Template, since it is about enforcing a rule on the URI, and the actual rule implementation is it is handled by the function ‘validateURINotUppercase’. The intent just provides human readable information about the rule.

The definition for this rule looks like this:

/*
Validate that the URI isn’t in all caps
@targets: Resource_URI_Template
 */
function validateURINotUppercase(data){
  if (!data || data === '/') {
    return true;
  }
  if(data.toUpperCase() === data){
    return  "URI '"+data +"' should not be all in capitals";
  }
  return true;
}

Let’s spend some time breaking down the structure of this function a little. The function takes a parameter, here a string representing the URI template, since that is what the target was set to in the rule JSON. It then applies some logic to that to determine whether the rule is satisfied, and should return true, where any violation of the rule should return a string. This string should be descriptive, as it is shown to the API designer in the Apiary editor.

Most rules are a little more complicated than this, as they involve more complex parsing, but have this same basic structure. This rule for instance should probably differentiate between URI parameters and literal components, as well as query parameters. Unfortunately there is no effective way to leverage external libraries to facilitate things like this. Everything needs to be included in the definitions section of the Style Guide, which means you could theoretically inline libraries, but doing so seems a little clumsy.

In order to developing these more complicated rules, I highly recommend taking a test-driven approach, since unit-testing the rules offline doesn’t disrupt anyone else’s API design. As the rule definitions are simply javascript functions, you can use whatever Javascript testing framework you prefer.

One of the complexities involved in writing unit tests for your Style Guide functions is in knowing what format your test data needs to be in. The data which is passed to the mapped function for each rule is based upon the ‘target’ in the rule JSON. Some of these targets are fairly simple, such as Request_Body, Response_Body, and Resource_URI_Template, etc. (A full list of targets is available on the Target dropdown list in the Mouse-Click mode of the Assertions tab of the Apiary Style Guide Screen) In these cases, the parameter is passed as a raw string, containing the relevant section of the API design document. While you can cover a lot of your requirements on these primitive components, there may be requirements to validate certain combinations of attributes about the API. Things like mandating that DELETE requests not have a request body (which mostly seems to be the convention because Apache HTTP server didn’t support it). This requires you to reference the higher-level API construct, rather than one of the raw elements.

Apiary provides two different object formats for accessing higher level API elements, through either ‘minim’ and through ‘API Elements’. Both are designed to be highly extensible and flexible in their definitions, which inevitably mean they are a pain to parse, especially since you cannot leverage external libraries.

The following snippet shows an example traversal of the ‘Action’ resource to facilitate the above mentioned ‘No request bodies on DELETE’ requirement.

/*
Validate that bodies are not expected on unexpected HTTP methods
@targets: Action
 */
function validateRequestBodyForMethod(data){
  const validMethodsWithBody = ["POST", "PUT", "PATCH"];
  for(var transaction of data.content || []){
    if(transaction.element === "httpTransaction"){
      for(var httpTransaction of transaction.content){
        if(httpTransaction.element && httpTransaction.element === "httpRequest"){
          var method = httpTransaction.attributes.method.content;
          for(var asset of httpTransaction.content){
            if(asset.meta && asset.meta.classes &&
               asset.meta.classes.includes("messageBody") &&
               !validMethodsWithBody.includes(method.toUpperCase())){
              return "Request body should not be sent used for " +method.toUpperCase() +" requests.";
            }
          }
        }
      }
    }
  }
  return true;
}

In order to determine the object traversal required, I had to obtain a copy of the object being passed to this function, and for that, the ‘Playground’ section of the Apiary Style Guide section is invaluable.

Here you can use some simple mock functions and rule definitions to output the API object formats you need to incorporate into your test cases.

i.e Mock Rule definition:

[
  {
    "ruleName": "Test Rule",
    "functionName": "testFunction",
    "target": "Action",
    "intent": "Just for testing object definitions"
  }
]

And function:

function testFunction(data){
    return JSON.stringify(data);
}

This allows you to generate different format objects for testing by changing the target of the rule, tweaking the API design and running the mock test, which allows you to simply copy the failure message (since the test function simply always fails echoing the target object) and simplify it for your test case.

The ability to create organisation wide design guidelines, which offer real-time feedback to everyone building APIs, is incredibly powerful, and helps to make your APIs look like the APIs of an organisation, rather than the APIs of a bunch of separate development teams. I have only recently begun dabbling with API Style Guides, and what I have at this stage, which demonstrates techniques which should cover a lot of basic scenarios, is available on GitHub here.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: