API style guide helps everyone to adhere to basic API design patterns and conventions.

Apiary Style Guide enables you to check multiple API description documents for consistency. The real benefit is the developer experience—consistency means predictability. Apiary Style Guide can be used within the Apiary.io web site or using Apiary CLI.

Check API's

With Apiary Style Guide, API designers receive real-time feedback during the API design process.

Real-time validation

Inconsistencies are caught very early in the cycle, helping you to avoid boring and expensive code refactor after APIs have been developed, deployed, and consumed.

API Design Assertion Language:

To a define a Design Assertion, two parts are needed: Functions Definition and Rules Definition

Definitions

Functions Definition:

A set of JavaScript functions used for validations.

/*
Checks if response to DELETE request contains body

@targets: Request
@minim: true
*/
function validateDeleteNoBody(request) {
  if (lodash.get(request, 'method', '').toValue().toLocaleLowerCase() === 'delete' &&
      lodash.get(request, 'messageBody')) {
     return 'DELETE request must not have a body.';
  }
  return true;
}

Each function has one input parameter - minim element if @minim: true annotation is present or (deprecated) JSON object found on desired path in refract tree. Function is not executed if no object is found. The function must return true if validation passes or a string which describes reason of failure if validation fails. Each function should have a block comment which describes its purpose. Comments can contain @targets annotations with comma-separated lists of allowed targets to which this function can be applied. All supported targets are allowed if @targets annotation is not defined:

Supported targets

api

meta
title
copy

resourceGroup
resourceGroup.title
resourceGroup.copy

resource
resource.title
resource.copy
resource.href
resource.hrefVariables

transition
transition.title
transition.method
transition.copy
transition.hrefVariables
transition.requestAndResponse

request
request.copy
request.messageBody
request.messageBodySchema
request.headers
request.header

response
response.statusCode
response.copy
response.messageBody
response.messageBodySchema
response.headers
response.header

header

Rules Definition:

A list of rules connecting function definitions to minim elements targets.

Each rule has the following attributes:

[
  {
    "intent": "All `id` keys in request JSON body object should be in format of UUID because ...",
    "ruleName": "request UUID id",
    "functionName": "validateUUIDInJsonIdKeys",
    "target": "Request_Body"
  },
  {
    "intent": "All `id` keys in response JSON body object should be in format of UUID because ...",
    "ruleName": "response UUID id",
    "functionName": "validateUUIDInJsonIdKeys",
    "target": "Response_Body"
  }
]

Real world example

We need to make sure that all APIs use the defined combination of request method and response status code for all API endpoints. Valid combinations would be:

  • 200: GET, DELETE, PATCH
  • 201: POST
  • 202: POST, DELETE, PATCH
  • 206: GET

Set up environment:

  1. install Apiary CLI
  2. get and set Authentication Token

Create examples

Create example API description documents that should pass and fail for your style guide - notice the POST request and 200 response combination:

FORMAT: 1A

# test FAIL

## test [/test]


### test post [POST]

+ Request (application/json)

        {
            "id": 1
        }

+ Response 200 (application/json)

        {
            "id": 1
        }

Save it to fail.apib.

FORMAT: 1A

# test PASS

## test [/test]


### test post [POST]

+ Request (application/json)

        {
            "id": 1
        }

+ Response 201 (application/json)

        {
            "id": 1
        }

Save it to pass.apib.

The more examples you create the better.

Develop a validation function:

We should choose the correct target at first. The Action target seems to be the most suitable for this style guide as we need to know/compare the request method and the response status code (Action element is the parent element of the Request and Response elements) and set up the rule:

 [
   {
     "functionName": "validateProperActionStatusCode",
     "target": "Action"
   }
 ]

Save it to rules.json file

And create the validation function itself (notice the code comments):

/*
helper function which validates correct combination of method and status code
*/
function _validStatusAndMethod(status, method) {
  //define valid combinations
  const statusCodeMap = {
    200: ['GET', 'DELETE', 'PATCH'],
    201: ['POST'],
    202: ['POST', 'DELETE', 'PATCH'],
    206: ['GET'],
  };

  //lets convert input to primitives if it is a minim element
  if (status && status.toValue) {
    status = status.toValue();
  }

  if (method && method.toValue) {
    method = method.toValue();
  }

  //look up in statusCodeMap and return true if given status and method is valid combination
  if (statusCodeMap[status] === undefined) { return false; }
  if (statusCodeMap[status].includes(method)) { return true; }
  return false;
}


// Validation function itself
/*
Validates proper combinations of request method and response status code.
Possible combinations are:
- 200: GET, DELETE, PATCH
- 201: POST
- 202: POST, DELETE, PATCH
- 206: GET

we'll specify `Action` as one and only allowed target because following code does not makes sense or would fail for any other target
@targets: Action
@minim: true
*/
function validateProperActionStatusCode(action) {
  //iterate over all transactions for given Action
  for (const transaction of action.transactions || []) {
    //check status code and method using helper method defined above.
    //notice that lodash lib. is provided but you are not allowed to require/import any other libraries
    if (!_validStatusAndMethod(lodash.get(transaction, 'response.statusCode'), lodash.get(transaction, 'request.method'))) {
      //return string describing reason of failing if validation has failed
      return 'No valid combination of status code and method';
    }
  }
  //return true if all validations have passed
  return true;
}

Save it to functions.js file

Lets test it!
apiary styleguide --add=pass.apib

All tests have passed

We should get exit code 0 and All tests has passed success message.

apiary styleguide --add=fail.apib


    validateProperActionStatusCode
      [X] FAILED: Action #0 on lines 7 - 19 - `No valid combination of status code and method`

We can also get the json output (for further machine processing) by specifying the --json parameter

apiary styleguide --add=fail.apib --json

We should get a verbose json output with all the available information included.

[
  {
    "functionName": "validateProperActionStatusCode",
    "target": "Action",
    "code": "function validateProperActionStatusCode(action) {\n  //iterate over all transaction for given Action\n  for (const transaction of action.transactions || []) {\n    //check status code and method using helper method defined above.\n    //notice that lodash lib. is provided but you are not allowed to require/import any other libraries\n    if (!_validStatusAndMethod(lodash.get(transaction, 'response.statusCode'), lodash.get(transaction, 'request.method'))) {\n      //return string describing reason of failing if validation has failed\n      return 'No valid combination of status code and method';\n    }\n  }\n  //return true if all validations has passed\n  return true;\n}",
    "functionComment": " Validation function it self\nValidates proper combinations of request method and response status code.\nPossible combinations are:\n- 200: GET, DELETE, PATCH\n- 201: POST\n- 202: POST, DELETE, PATCH\n- 206: GET\n\nwe'll specify `Action` as one and only allowed targets because following code does not makes sense or would fail for any other target\n@targets: Action\n@minim: true\n",
    "allowedPaths": [
      "Action"
    ],
    "minim": true,
    "ref": [
      "Action-0"
    ],
    "results": [
      {
        "validatorError": false,
        "result": "No valid combination of status code and method",
        "path": "Action-0",
        "data": { ...truncated... },
        "sourcemap": [
          [
            43,
            168
          ]
        ],
        "sourcemapLines": {
          "start": 7,
          "end": 19
        }
      }
    ]
  }
]

Notice the result key in results array - we just need to see if validation has passed or not.

If you are satisfied, you can add the new rule and function(s) to the Apiary style guides for your team by navigating to Team settings -> Styles -> Assertions -> Advanced mode. The rule is applied for all team APIs from this moment:

Editor validation

Related topics