This ramble was triggered by a discussion we had at work. Essentially it revolves around – “What makes an API response structure good?”. More specifically – a JSON response object.
Preface
Well then, hang on to your hats, we’re going on a mad monologue.
Let me start off by saying – much smarter, and more competent people have discussed this, and have developed a thing called standards. If you’re looking for a more professional standpoint – click here: API.org
API Response objects
What on earth is an API response object, I hear a few of you ask? It can be summarized as – a pre-defined structure, populated by data, that is returned to the client, upon querying an endpoint.
Whoa, much words, very wow. Cool, to expand on that – a JSON response is the thing described above, modified to be returned as a JSON object. This is quite useful, as API clients can read a ton of information, squeezed into a tiny package. And, by using a simple formatter – you can read the response quite easily, as well. Here’s one I use on a seldom-daily basis: Link
What should it contain
Here we go with the wild speculation, and the personal opinions. Generally a JSON response object should contain the data that you want your client to receive, a duplication HTTP status code and a message of some sorts.
Why? Well, the data part is quite obvious, a duplication HTTP status code can serve many purposes. One of which is – getting a clear idea of what is going on, without having to examine the request data. Another key use case exists if you process your received messages, on another machine. Let’s elaborate:
You have your client and your server. Your server sends responses back to the client, on a request. You then store the messages for safe-keeping and analysis. With an included HTTP status code, you can store the whole thing, compressed and all, and be done with it. Six months down the line, you can review the specific response object, and you have the whole lot of information at your fingertips. If you don’t include the status code, you have to parse your response header/status information, format it, attach it to the returned data block, and figure out a way to store all of this efficiently.
Messages
The inclusion of this section is a key feature in something that I call “computer and human readable”. Essentially, you want a response block to be readable by both a meat and a wire machine. That’s it, that’s the justification.
Meta data
Erm, right, so this is where it gets interesting. Metadata can be any information used to support the original data block. A super simple example is – pagination. You include the current page, next page, count etc. These pieces of information don’t make up the data block the client is expecting, but rather they help support it. When and how should you include it? Well, it depends on the use-case, but we’ll get to that in a moment.
The grand conclusion
Here’s what I think a response object should be made out of:
Success response (200, 202, 203) etc.:
{
"status": "success",
"code": 200-203,
"message": "HTTP Status Message",
"data": {}
}
Where, the data section is optional. It really depends on what you’re returning
Success response with meta:
{
"status": "success",
"code": 200-203,
"message": "HTTP Status Message",
"data": {}
"meta": {}
}
Same thing as above, only this time both the data and meta blocks are compulsory, as you’re returning something with a supporting meta block (duh..)
Failed response (400, 422) etc.:
{
"status": "fail",
"code": 400-499,
"message": "HTTP Status Message",
"errors" : {}
}
This is a similar block to the 200 one. With the one change – this time we’re returning an errors block. Within Laravel for example, you can return all your validation messages here.
Error response (500):
{
"status": "error",
"code": 500,
"message": "HTTP Status Message",
"errors" : {}
}
The same as the 400 one, but the status indicates that an error has occurred.
Note that the 400-500 range responses don’t include a meta block. In my opinion, you really shouldn’t have a meta block here. If something has happened, the client should have all the required information stored, to proceed. This may include the originating (previous) page etc. If you really have to send a re-direct link, for example, use a 300 range message. Here’s an example:
Re-directs (301):
{
"status": "re-direct",
"code": 301,
"message": "HTTP Status Message",
"data" : {}
}
Place your links in the data, and forget about them. I don’t see why you should overcomplicate your life with juggling meta and data blocks with a simple redirect.
HTTP Status Messages?
Yep, standard stuff. You can find massive lists like this one and this one. Just use them programmatically, or dynamically – if you’re a guru.
Wrapping up
Remember, the purpose of a cleanly designed response object is – simplicity. Don’t overdo it, and you should be fine. Obviously, in an enterprise environment, if you can, make sure that the whole company is using the same standard – or convention. This will enable the front-end and mobile teams to talk to your magnificent API.
Have a good one folks!