Do you ever have those moments when you’re asked to do something that feels technically wrong? Putting milk in the cup before the tea? (a very British problem admittedly). An overflowing trash can YOU must always empty? We all have our pet peeves — it’s what makes us human.
In technology these pet peeves are normally writ large — and we love to pick holes and question other people’s decisions. You use that framework? You name your variables with underscores? Really? Stack Overflow thrives on this kind of stuff and it’s no surprise that in the world of APIs it’s just as exacerbated. You don’t do hypermedia? Why is there a verb in your URI? XML not JSON? Opinions are like the proverbial backsides — we all have them, and we all dish them out at will.
However, it goes without saying that there are times when you, an IT professional who loves their craft, will do what they feel makes the most sense in spite of these opinions. Or, perhaps you’ll be in a situation where business requirements necessitate practices that aren’t exactly “best.”
In this post we look at a few patterns and anti-patterns in API design and discuss their implications on the human beings that have to implement them. We’ll cover how a pragmatic REST attitude can retain technical acumen while allowing leeway for business needs.
No Hypermedia? That’s Not REST
We’ve discussed HATEOAS (hypermedia as the engine of application state) on the Nordic APIs blog plenty of times before. Technically, in the view of the creator of REST and many others, an API is not RESTful without hypermedia. If you consider most APIs in this vein, not including a HATEOAS approach in your API design is the greatest API antipattern of all.
Regardless of how “right” implementing HATEOAS in an API is, there are pros and cons that the Nordic APIs blog has discussed in a previous post. We won’t cover these again here, other than to make one comment: Despite all the good things HATEOAS can deliver to your API consumers, implementing it using the tooling available to the majority of API designers isn’t a walk in park. Moreover, for consumers of your API the implementation of HATEOAS may actually dilute the simplicity you’re striving for as an API designer. The referenced article phrases this as such:
“…implementation is of course entirely contextual to the situation at hand – for APIs that do not wish to adopt HATEOAS or hypermedia, the lack of adoption can be seen as much a feature as a drawback. In those situations, especially in the case of failure to implement hypermedia at all, these APIs should not be considered or called RESTful per the creator’s own definitions.”
The key lesson here is don’t be afraid to call things by their real name and be prepared to describe your design intentions correctly. One phrase is “pragmatic REST, meaning an API that adopts most of REST but not all of it. This phrase is no longer in vogue but many APIs still exhibit the same characteristics. Alternatively there’s no heresy in creating a remote procedure call (RPC) API if it genuinely describes what it is AND it’s what your customers want.
If it ain’t REST — don’t call it that — but don’t get too upset if somewhat calls out a self-acclaimed “RESTful” design that’s not true to REST.
A POST where a GET will do
There are some instances, however, when you attempt to embrace technical purity and encounter internal resistance. Unfortunately, as an API designer there may not be much you can do about it.
Let’s take the case study of Dave the API Designer. Dave’s started a new job at a financial service company — let’s say a large bank. He gets cracking using all his design knowledge to start sketching out a new Customer Enquiry API using Swagger Editor and it’s going great. At the end of the first sprint Dave takes the design to show-and-tell to discuss the specification with his peers and stakeholders. The session is going well and everyone buys into his design.
Suddenly an arrow, straight to the heart, fired by someone from IT security: “Dave, I get what you’re doing with your GET on the Customer resource, but we don’t do that here. Can you make it a POST instead?” Dave’s gut reaction is “what the very ****” but he manages to suppress this response and reply with a simple “please explain?”. Sure enough, the security team explains:
They tell Dave:
- We have many systems that sit between your API at the API Gateway and application that will expose the API on our PaaS. All of them are logging requests in some way.
- One of things that is often logged is the URI.
- If the URI is logged it might contain query parameters that contains sensitive data.
- This increases the organization’s exposure to unwittingly breaking regulations such Payment Card Industry Data Security Standards which guard the privacy of such data.
- We know that the logging platforms never log the payload, so the sensitive details need to be placed here.
- This is the standard we’ve implemented and signed-off for this kind of APIs. We are not prepared to change the standard based on the cost to reflect the logging practice in all our systems.
Dave’s internal voice of reason is shouting “NO!!!” and clearing his desk of laptop, screen and his prize collection of Guardian’s Pop Funkos. However, the rationale side of him is forced to acquiesce to get the design signed-off and he has no reasonable arguments about how this will affect either the functionality or the experience for developers.
The lesson here is: We all want to be REST aficionados and design our APIs to the letter. However, sometimes that flies in the face of what’s right for the organization we work for. As API designers we may need to make concessions based on organizational context rather than best practice. It ain’t RESTful – but it will get the job done – and make stakeholders happy.
An Idempotent POST
When discussing idempotency (when an operation will have the same effect whether executed once or multiple times) the majority of API designers will simply assume the rules of HTTP as per Fielding’s direction. Seems straightforward enough, right?
Well, yes and no. Whilst the definitions of the methods themselves are enshrined in the HTTP standard, API providers often bend the rules to suit their needs. An idempotent POST therefore becomes a concept, especially in payment API scenarios. The API provider wants to ensure that if an API call to make a payment fails during execution – such as in a timeout scenarios where no discernable HTTP response is returned to the client – subsequent attempts do not result in a duplicate payment instruction being submitted. Idempotency is achieved by requiring the client to submit exactly the same instruction with an identifier that signals to the API provider that they might have seen this instruction before.
The reason behind this design approach is simple: API providers want to ensure that API consumers don’t duplicate payments, on the basis that this has an effect on real people i.e. the API consumers valued customers. There are a number of examples of this approach, notably PayPal and the UK Open Banking Payment Initiation API.
An idempotent POST – whilst not being RESTful due to the fact it ignores the rules of HTTP – brings a lot of value to the API consumer in helping them deliver a great payment experience to their customers. In cases like this pragmatic and deterministic design of the API as a product – defending the API consumer from mistakenly submitting multiple payment instructions – takes precedence over technical “correctness” for many API providers.
A Consumer? Well They Should Be an Entity
Many designers laying down their API specifications for the first time have a tendency to take the idea of entities as espoused by REST and turn everything into an entity ruled by one API. This includes the API consumer who is calling the API, which is an approach leads to a specification that incorporates URIs like this:
For the designer this might make perfect sense. However, there are many reasons why this kind of construct isn’t a great practice:
- It’s significant only for the API provider not the consumer – the consumer (generally speaking) will only be interested in themselves and no-one else. This results in more complexity in the URI which delivers zero value for the consumer.
- The designer is mixing the security model and data model by requiring a consumer to identity “themselves” in the URI. This leads to an interface that is both more brittle and also subject to potential abuse i.e. if there’s an issue with the security model wrapping the API a miscreant may be able to address other clients customers once they are past the security checks. Whilst this style of API is becoming less common for API-first companies, organizations that are more inward looking still consider this design choice.
- It introduces some nastiness around nesting resources, especially in view of a versioning strategy. As Zdenek Nemec points out this approach – that he defines as the “fallacy of nested resources” – can lead to a complex interface that developers will find difficult to comprehend.
The key point here is: Model what you need to model in your APIs – not other stuff that might be associated with it. If there is a need to overtly relate one entity to another then make sure you use the HATEOAS approach (or even consider GraphQL…) rather than using the brittle construct of nesting resources. It’ll make your API cleaner and easier to understand.
How to Remain Sane With Pragmatic REST
Let’s face facts – some parts of this post will have you either nodding sagaciously, agreeing with the points made, or grinding your teeth and ready to punch a hole in the nearest whiteboard. There a bunch of other things – versioning in the URI, for example – that we could get into that would be equally divisive.
However, in this still formulating world of what good API design looks like we could all learn something if we document our design decisions – so everyone can see why we implemented a solution that way – and do our research and look at what’s gone before.
Above all else, we as technicians need to learn to chill. Requirements and people will come along that force us to implement a pattern or anti-pattern we don’t like. A sense of perspective can therefore be a very useful asset – so try to remember, it’s all just ones and zeros.