How To Design A Good API

YourAPIExpert Essential Series
How Do I Implement A Good API?

In the previous section we discussed the practical aspects which make for the good design on an API but a good design can often be let down by a bad implementation.  In this section we will discuss the fundamental aspects of a well implemented API.

Start Small

The concept of starting small is synonymous with most things in life.  When cooking it is always easier to add more spices to taste but not as easy to remove spices if there is too much.  Similarly with an API, particularly a public facing API, it is always significantly easier to add functionality than to remove it.

To this extend the API must start with its smallest viable set of functions and functionality.  It should be only as powerful as the specifications call for and must satisfy all of its requirements.  This places great onus on the developer to not overthink the API or add functionality albeit perceived because once it is in use it is difficult to undo or remove.

A small API is also a flexible and nimble API which can adapt quickly to new requirements driven by changes in the market or internal innovation.

Be Consistent

An API is generally quite small and leaves little room for ambiguity and misinterpretation.  Consequently there is a strong desire for everything to be consistent.

Response codes should be consistent and should mean the same thing across APIs.  For this reason most developers make use of the standard HTTP response codes often adding additional information as to the cause, for example:

    "status": "Bad Request",
    "message": "The selected method (PUT) is not supported by this API."

Similarly the same words should mean the same thing across APIs with respect to parameter names as well as documentation.  This will avoid misinterpretation, uncertainty and ambiguity.

Standardize on both a naming convention as well as how the naming convention is represented.  To conform to industry norms the use of lower camel case is mostly prevalent in RESTful APIs.

Protect Information

At times an API can supply too much information.  Information which poses a risk to both the organization and customer.  Consider this horrific real example:

    "reason":"ERROR 1045 (28000): Access denied for user 'bluebird'@'localhost' (using password: YES)"

Against all odds this API was placed in to production at a small organization accepting orders for the pick-up of laundry.  Everything was great until a system upgrade forced this message to be returned on everyone’s mobile app.

The application was lazily coded to display text in the “reason” parameter of the response in as friendly a manner as possible to the customer on the assumption that the back-end developer would intend this reason to be displayed.  The application did not have a preference for the context of the reason only that it would generate the informational display should it exist.

The back-end developer who was seemingly oblivious to this fact at one stage became tired of predicting errors and setting traps in their code to catch and handle these errors.  Consequently the developer decided that whatever errors remained were so obscure and unlikely that they could handle these with a generic catch-all mechanism to display the error for later debugging.

The unintended consequence is that when the database became unavailable after the system upgrade the generic catch-all mechanism caused too much information to be made available to the customer.  Instead of merely saying something like “Service Unavailable” the databases locations (localhost) and authenticated user was exposed opening the door to a litany of problems chief amongst them being security.

Of the more prevalent information leaks the following two stand out:

    "error": "Incorrect password"
    "error": "That user does not exist"

For any person wishing to obtain usable credentials these error messages are gold.  The first example helpfully explains that although the password was incorrect the username was valid and legitimate.  It only requires a matter of time, a rainbow table or brute force attack to derive the correct password.

The second example is extremely useful too in that it can be used to test a dictionary of common names against the service to determine a legitimate username as a starting point.

In both cases the error message could have been specified as follows:

    "status": "Unauthorized",
    "message": "Unauthorized"

This is an infinitely better error response than the above two examples.  Firstly it gives absolutely no hint as to the reason for the denial of access and secondly it exposes no internal mechanics of the authentication scheme.

Be Modular

An API is not a single piece of code.  Rather it is a collection of reusable software modules and plug-ins very much in line with traditional object-oriented programming techniques.  It would thus stand to reason that since an API is piece of software that its design should very much be modular.

By following traditional OOP design the developer is able to leverage the traditional benefits of such design.  The modules are reusable across APIs which cuts down on development time and complexity significantly.  Similarly as the modules are self-encapsulating they become the subject of unit tests which adds to their predictable and robust nature.

Being modular also means that the API’s internal can be perfected, extended or even be replaced over time so long as it does not require significant changes on the Northbound (public) interface.

In the next few articles we will be using this information as we head in to the final few practical articles in this series.

Was This Helpful?
All of the content on this site is presented without advertiser support and is produced exclusively by me. If you find any of this information useful please consider it against the cost of a course or book.

I gladly accept donations of any amount which goes directly towards producing more quality content and videos for this site.

[wpedon id=119]


Pages: 1 2

Written by YourAPIExpert