Article was written by Jens Grassel, author of the Educative course “Pure Functional HTTP APIs in Scala”.
If you’re building an API (we assume an HTTP or REST API here) for the first time then there are several areas that might get less attention than deserved. In turn, these can be sources of problems later on.
Today, we’ll put a spotlight on some of these problem areas to help you avoid unnecessary trouble.
Here are the pain points we’ll cover today:
At first, I want to write some thoughts about API versions because I still see this neglected or ignored way too often.
Versioning, sometimes called version control, is the process of designing software to anticipate future updates and minimize the work needed to update the system. The best forms of versioning anticipate that both your system and external systems will change. Examples could be things like avoiding hard coding whenever possible or including methods to check for external resource versions before it’s used.
Some people might argue that their API is “complete” and that there will be no changes. However, this is rarely true.
If the software lives long enough, then it will change. This is also a good opportunity to remember that the amount of time we spend on maintenance far exceeds the amount for developing new systems. Just imagine your bike has a flat and instead of fixing that you would create a new bike from raw materials every time that happened.
Philosophies of how we should version our API differs a great deal, but it is more important to consider an evolving API already in the design and implementation phases.
There are two common ways to implement versioning: URI versioning and HTTP headers.
This is the most commonly used practice and it is quite straightforward. You simply use a different URI for your API versions. Quite often you see things like
https://api.example.com/v1/ or even
The obvious downside of this is that access paths will change with every version. On the positive side: This schema is usually very easy to implement and follow.
Sometimes it is considered most important that the URI to a resource never changes. In this case API versions might be requested by HTTP headers. You can either introduce custom ones (e.g.
Accept-API-Version: v1) or abuse err use existing ones like
The nicety of not having to worry about changed paths comes with a price tag though. Using this kind of versioning is less obvious to follow and also you have to ensure that an existing endpoint supports all existing versions.
This one should be clear but I cannot stress it enough. Why? Well, I’m simply unable to count the number of APIs I’ve seen which will happily return an HTTP status code of 200 and some unclear error message in the response body.
Why is this bad? Well, it assumes that a human user using a web browser is accessing your API. This sometimes helps with debugging but more often it just complicates things on the client side. The HTTP status codes are there for a reason, so it’s best to put them to good use. You might not find one for every nuance of error but try to add enough to clearly indicate an error to the client.
Do not forget to include useful error information/messages in your response. Things like the last step completed in a process, the time of the error, and the name of the failed component are all good choices. These will save your users a lot of time when working through issues and earns you positive credit with them. However, there are corner cases in which you might not want to include detailed error messages due to privacy or security concerns. In general, include as much information as you can without sharing sensitive information.
Another best practice is to ensure that you’re consistent in your error message format. If your message is text, it is alright to return only text. On the other hand, if you want to have more specific information, such as metrics, it’s best to have a clear distinction between the two. Take your time to design an error response model which can be parsed by the client and you can consistently use it across your system.
Last but not least let’s talk about authentication.
Authentication is the process of verifying a user’s credentials before they access a given functionality or set of data. It is closely related to authorization, which is the process of securely passing the appropriate credentials to a new user. In short, authentication systems verify credentials, authorization systems grant the appropriate credentials.
Both are a fundamental part of access control, a category that consists of any systems that ensure only authorized users can access resources at any given level.
The specific implementation of authentication depends on your use case but it’s best to give it some thought from the start. Simply “bolting on some authentication layer” after release is a sure recipe for trouble.
As usual, there are many ways to tackle this problem like basic HTTP authentication, OAuth2, JWT, API-Keys in Requests, and likely more.
Choose wisely depending on your needs because some are more secure but also more involved than others. For simple cases such as if every transport is encrypted(!), basic authentication or API keys can go a long way. However, things get more involving if different services interact (think service orientated architectures). In the latter case, you’ll be better off with OAuth2 or JWT.
As you move on to build your next API, the best advice is to build for the future as well as the present. Keeping these tips in mind should ensure that you’re considering future pain points and mitigating them off the start.
Some other concepts you should look into are:
To help you practice your API skills, I’ve created my Educative course Pure Functional HTTP APIs in Scala. This course helps you combine functional programming and HTTP service design to create powerful back-end APIs. You’ll get hands-on experience with different pure and impure implementations as well as industry-ready benchmarking techniques to check your work. By the end, you’ll have all the tools you need to develop APIs using functional Scala.
Join a community of 500,000 monthly readers. A free, bi-monthly email with a roundup of Educative's top articles and coding tips.