sUnit Blog
insights on software development
Guidelines on JSON responses for RESTful services

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.

API

Responding success and failure

First question that comes in discussion is, How shall we craft response for success and failure? Different options are,

  1. Use HTTP status codes conveying the status + json body in response payload
  2. 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?

  1. 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
  2. return only data with HTTP status code and use response headers for count fields
  3. 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

  1. camelCase
  2. snake_case
  3. 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,

  1. Do not return NULL values
  2. 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

  1. as array of strings
  2. 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,

  1. How to represent structural data? nested vs flatten
  2. Define standards for paginated results? (params and result both)
  3. Singular vs Plural Property Names?
  4. If needed, how would you version your API and how to return response with version info ?
  5. 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