Home/Blog/Programming/3 Features beginner devs overlook when building APIs
Home/Blog/Programming/3 Features beginner devs overlook when building APIs

3 Features beginner devs overlook when building APIs

Jens Grassel
Oct 24, 2023
6 min read
content
Versioning
URI versioning
HTTP headers
Error codes and messages
Authentication method
Closing thoughts
Continue reading about APIs
share

Become a Software Engineer in Months, Not Years

From your first line of code, to your first day on the job — Educative has you covered. Join 2M+ developers learning in-demand programming skills.

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:


Master HTTP APIs in half the time

Learn to leverage functional Scala to create cutting-edge APIs for your next project.

Pure Functional HTTP APIs in Scala


Versioning

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.


URI versioning

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 https://api-v1.example.com/.

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.


HTTP headers

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 Accept: application/vnd.example+json;version=1.0.

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.


Error codes and messages

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.


Authentication method

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.


Closing thoughts

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:

  • Delivering your content using HTTP content negotiation to return data in the format needed by the client
  • Caching! Yes, look at your use case and consider returning proper caching headers to avoid clients polling your endpoints too frequently
  • If you’re using true REST, take a look at Hypermedia as the Engine of Application State (HATEOA)

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.

Happy learning!


Continue reading about APIs

Frequently Asked Questions

What is considered a good API?

A good API provides consistent, well-documented functionality. Ideally, it should have an average response time between 0.1 and one second, ensuring swift data exchange and a seamless user experience.

What is an API for beginners?