What makes a back-end service (that exposes functionality in the form of RESTful APIs) fun to work with? Not just for the devs working on the service but also for everyone else using it, including but not limited to developers who consume those APIs. I think this question is more valuable if flipped: What makes working with a back-end service a nightmare? What feels like you are fighting the service at every point in your code?
In this post I want to draw on my experience building API services and talk through a few considerations I like to look into while building back-end services. Some of these could be provided by the framework itself, while others might be functionality worth building!
Consistent API naming that includes everything API-related: paths, requests, responses, and headers! This allows working with APIs more easily and sets a nice, consistent pattern for all new APIs. Teams that consume this API will see a more predictable pattern that makes consuming the APIs easy.
Consistent Error Structures
I dedicated a whole section to error responses because this is probably one area I wish more APIs prioritized consistency for! It is hard to work with services that don’t pass back a consistent error response for all APIs. It is possible to have certain fields in the error response be absent or present depending on the particular error, but having the same overall structure makes working with these APIs very easy because consumers of the API can dedicate a central location to handling errors vs. having to handle API-specific errors. Similarly, devs building these services can create and use error handling at a global level vs. having different error responses come from different sections of the back-end service.
Logging is the core of any software application. Back-end services are no different! It is important to identify all the places that require logging and pick the right log level, which makes reading logs easy using tools like Splunk (easier to filter all the error level logs vs. the debug logs, etc). Along those lines, it is more important to make sure the things that are logged DO NOT contain sensitive information of any kind. This includes, but is not limited to, Personally Identifiable Information (PII), API keys, authentication credentials, etc. It is extremely important to make sure these values are not logged in plain text because that is a trap that is easy to fall into and could bring unwelcome consequences.
When something goes wrong, more often than not, it helps to know the entire journey of a request: Where it came from, what came in, what time was the request received, what the back-end service did to handle that request, what other API services (if any) were called, etc. The ability to trace a request all the way from what was received to what was sent back helps in keeping track of request handling and debugging.
Support for Secrets
There is going to be at least one secret that your application is going to need unless you are building a “Hello World” application. When I say secrets, I mean everything that is sensitive that should not be exposed to the whole world — anything you would never check into source control. API keys, secret values to access other services/applications, signing keys, expiration times, username/password credentials, etc. When building a back-end service, it is important to make sure that the app or the app and the host provider (if your app is going to be deployed to the internet) have some kind of support for secrets. This includes being able to host and store secrets in such a way that only authorized personnel can manage those secrets. Some platforms take this a step further and also provide the ability to rotate secrets on a regular basis. This is very powerful because this saves back-end service from being the weakest link in the chain. Noteworthy while we are on the topic of secrets is to never (ever) commit your secrets to source control. If that happens, there are tools to revert those commits and remove them from source control, but in some cases it might be easier to just regenerate that secret and invalidate the old one.
Support for API Documentation
Documentation is the bridging language between everyone who consumes a back-end service and the rest of the world. While picking a framework to build your service or as you are building your service, it is important to think about how all the APIs are going to be documented. This should be usable by any team that wants to consume your APIs. Swagger hub is a very popular and widely used tool. Other options include Spring Rest Docs if you are building your service on a Spring stack. If the generation of documentation is closely coupled to the code itself — meaning any new updates to the code will automatically update the documentation — that would be an added bonus because it relieves developers from having to constantly keep the code and documentation on par.
Software solutions are built for people, by people. Making sure your application is easy and friendly to use, not just for the end users but also fellow teammates goes a long way in ensuring a solid product that can be iterated over successfully if the right patterns are put in place starting as early as possible.