Now days RESTful API is followed heavily on most of the projects. Since it doesn’t prescribe every details, many times I have seen developers start following different ways of implement for similar patterns in API, leading to inconsistent API. So during initial stage of the project it is very important to discuss and define guidelines for RESTful APIs. In this article I am focusing on JSON response guidelines.
Responding success and failure
First question that comes in discussion is, How shall we craft response for success and failure? Different options are,
- Use HTTP status codes conveying the status + json body in response payload
- always return 200 and response payload to have status and data fields.
My take is use HTTP status code to convey the response status. And I prefer not to create envelope response but return requested object at top level JSON. And for errors return appropriate HTTP status code with more detailed error message and errors as JSON response.
Status: 200
Content-Type: application/json
{
"employeeId":123456,
"firstName": "Sunit",
"lastName": "Parekh",
"gender": "Male",
"hireDate": "2012-01-01",
"birthDate": "1990-01-01",
"office": "Pune"
}
Status: 404
Content-Type: application/json
{
"code": "NOT_FOUND",
"message": "Oops! Requested employee 123456 not found."
}
Status: 400
Content-Type: application/json
{
"code": "INVALID_INPUT",
"message": "Oops! Invalid data provided for employee."
"errors": [
{
"message": "Last name is required field.",
"field": "lastName"
},
{
"message": "Age of the employee should be greater than 18",
"field": "dateOfBirth"
}
]
}
Status: 401
Content-Type: application/json
{
"code": "UNAUTHORIZED_ACCESS",
"message": "Unauthorized access for the requested content."
}
Effective use of HTTP status code helps with leveraging HTTP caching and coding clients. Also it is important to discuss and decide which status code to use when. Read more here about status codes.
List and search result type of responses
Next question comes up is, how to return list or search result like responses? This is the heavily debatable and many times inconclusive topic for whole team to agree. Okay, what are the options?
- use envelope style response, result includes count plus data, however for error conditions such as bad input or un-authorized request use appropriate HTTP status code
- return only data with HTTP status code and use response headers for count fields
- always return 200 and status in response body
My take, here I am divided between option 1 and 2. Still preferred option is Option 2, return only data and convey summary fields using headers. Another alternative to sending count field in response header is have separate count API. This is also useful many times as you need count only once.
Status: 200
Total-Count: 100
Next-Page: true
Content-Type: application/json
[
{
"employeeId":123456,
"firstName": "Sunit",
"lastName": "Parekh",
"gender": "Male",
"hireDate": "2012-01-01",
"birthDate": "1990-01-01",
"office": "Pune"
},
{
"employeeId":123457,
"firstName": "Sunit",
"lastName": "Parekh",
"gender": "Male",
"hireDate": "2012-01-01",
"birthDate": "1990-01-01",
"office": "Pune"
}
]
Status: 200
Total-Count: 0
Next-Page: false
Content-Type: application/json
[]
Use of response header is key in the above example. Next-Page response header tells client that I have more data (next page) available. And such headers can be really useful for loading next pages on scroll.
However, when I look at API like elasticsearch I get convinced that Option 2 is also right. Choose one for the project and follow it.
JSON key (property/field name) naming convention
Now it’s time to get inside the JSON fields. What should be naming convention for fields? Options are
camelCase
snake_case
spinal-case
My choice here is camelCase
and that must be from my background as Java developer. I have seen equal use of snake_case
as well, but very rare use of spinal-case
.
Status: 200
Content-Type: application/json
{
"employeeId":123456,
"firstName": "Sunit",
"lastName": "Parekh",
"gender": "Male",
"hireDate": "2012-01-01",
"birthDate": "1990-01-01",
"office": "Pune"
}
Avoid using dot (.) in key names, as they conflict with the JSON path.
Handling NULL values
It’s time to look at, how to return NULL values? This is again another area of debate starts. Options are,
- Do not return NULL values
- Return as null value
I prefer Option 1) Do not return NULL values. However, question becomes tricky when value is object or array and it is empty. In that case empty {} or [] is valid JSON response.
// Response with null and empty values
// -- NOT RECOMMENDED, JUST FOR REPRESENTATION PURPOSE --
{
"employeeId": 12345,
"firstName": "Sunit",
"lastName": "Parekh",
"mobile": null,
"addresses": []
}
// Response without Null Values, Empty object or array is still present
{
"employeeId": 12345,
"firstName": "Sunit",
"lastName": "Parekh",
"addresses": []
}
// Response without Null and Empty Values
{
"employeeId": 12345,
"firstName": "Sunit",
"lastName": "Parekh"
}
I do not prefer null values as it can have side effects while parsing on client side. E.g. In JavaScript null has special meaning. Just be careful not to choose returning empty string (“”) which does not express as value doesn’t exists.
Date and DateTime responses
This one is most important, What should be format for date and datetime (timestamp) type of fields? Option here is ISO 8601 format returned as string. However, many time not knowing it, make developer to follow “default toString” or “self-made formats”. Most of the languages have default support for serializing and deserializing, date and datetime in ISO 8601 format.
// audit fields
{
"createdBy": "123456",
"createdAt": "2012-01-01T18:25:43.511Z",
"createdBy": "123456",
"createdAt": "2012-01-01T18:25:43.511Z",
}
Even if you choose to return datetime always in only one timezone such as UTC. I still recommend to choose ISO 8601 standard which includes timezone.
Multiline string values
Many times I need to send large multiline string as values e.g. Address. How do I pass multiline string value? Options are
- as array of strings
- base64 encoding
Solution that worked for me is base64 encoding for values.
// multiple as array
{
"singleLine": "Some singleline String",
"multiline": ["Line one", "Line Two", "Line Three"]
}
// multiple as base64 encoded string
{
"singleLine": "Some singleline String",
"multiline": "TGluZSBvbmUKTGluZSBUd28KTGluZSBUaHJlZQ=="
}
Another example is to use base64url
encoded query param for GET request providing search criteria as JSON. :-)
{ "name": "sunit", "office": "pune"}
/* above search criteria parameters send in GET request param as base64url encoded */
/search?query="eyAibmFtZSI6ICJzdW5pdCIsICJvZmZpY2UiOiAicHVuZSJ9"
Summary
And there are many more discussion and decisions to be made for JSON responses guidelines, such as,
- How to represent structural data? nested vs flatten
- Define standards for paginated results? (params and result both)
- Singular vs Plural Property Names?
- If needed, how would you version your API and how to return response with version info ?
- Think about more data types? e.g. Latitude/Longitude values
Point I would like to derive from this article is, at the beginning of the project collectively define guidelines for RESTful services for the project and write it down. Also keep it open for discussion and further amendments.
Another new trend to explore is GraphQL for building API that needs to be consumed by many discrete client needs.
Last modified on 2018-04-03