Making web service APIs that play nicely

We are part of the way through writing stories for the next Silver Squid project and I thought I would share some of these to do with the basic requests and responses of our web service APIs.

Choosing Data Types

Something that I feel is important, is to allow the user of your APIs to send and receive data in the format of their choice. They may be trying to integrate your system with one that only speaks one language, or they might be better versed in some than others.

With that in mind, our latest project allows the user to set whether they are sending data as a normal form submission (necessary in this instance, to allow the user to send files), as XML or as JSON. We allow these to be set in HTTP headers, so that they are set before the system examines the sent data at all. The story is as follows:

As an API user
I want to be able to send HTTP headers defining my request and response data types
so that I can better integrate the API with my system

Acceptance criteria:

  • M: A requestDataType header can be sent – the values can be: form, xml or json. This should dictate the expected data and default to form: if not provided.
  • S: If requestDataType is set to a different value the error ERR_UNSUPPORTED_REQUESTDATATYPE should be thrown.
  • M: A responseDataType header can be sent – the values can be: xml or json. This should dictate the response data type, and default to xml if not provided.
  • S: If responseDataType is set to a different value the error ERR_UNSUPPORTED_RESPONSEDATATYPE should be thrown (in XML).

I have begun to employ the MoSCoW Method for acceptance criteria priorities. According to Wikipedia – these are defined as:

M
MUST have this.
S
SHOULD have this if at all possible.
C
COULD have this if it does not affect anything else.
W
WON’T have this time but WOULD like in the future.

It is very helpful to have a convention to follow for things like this, and this method seems as good as any other.

Consistent Errors

You’ll see that I have defined the errors that will be thrown, and specifically stated that when the Response Data Type cannot be inferred, to use XML.

I try to always be very specific about the error that will be thrown, and to throw those errors in a consistent manner. It feels natural to me to throw HTTP errors when our API fails, and for a while I tried to always throw the appropriate HTTP error code. I gave up on this, because there just is not always an appropriate error code. Now we always throw a 500 error, and provide a string describing the specific error, in the format requested by the user. The story in this project that defines this is:

As an API user
I want errors to be thrown in a consistent fashion
so that my system can detect these and respond appropriately

Acceptance Criteria:

  • M: When an error is thrown, the system should return an HTTP error code of 500.
  • M: When an error is thrown and responseDataType is set to xml (or missed out and thus defaulted), the error should take the following format:
    <?xml version="1.0" encoding="UTF-8"?>
    <rootNode>
    	<error>ERR_CODE</error>
    </rootNode>
  • M: When an error is thrown and responseDataType is set to json, the error should take the following format:
    {"error":"ERR_CODE"}
  • S: All error codes should begin with ERR_.
  • S: A default error of ERR_UNSPECIFIED_ERROR should be thrown when we can not further isolate the error.

This should make it easy to predict the error formats, and also make them easier to write. Knowing that all errors will begin with “ERR_” allows the user of our API to easily handle for all errors, and add more specific handlers when/if necessary, although checking for an error property or node is enough to detect that an error was thrown.

I hope this has been useful. How do you deal with errors and data types in your APIs?

2 thoughts on “Making web service APIs that play nicely

  1. A lot of my PHP code will throw an exception and I used to send the exception description onto the API user, it can be useful… but it’s information the user shouldn’t really see, if they’ve got a list of possible error codes they can handle the error appropriately on their side and/or display the correct information to their users. Plus it prevents a crap load of unnecessary data being sent across.

    For my response I always send back a status value, so my response would look something like this.

    {
    “response”: {
    “status”: 0,
    “error”: “ERR_NODATA”,
    “data”: {all my response data}
    }
    }

    1. Why wrap the entire thing in a ‘response’ property? Your way does provide more data, and lots of people suggest always having a ‘status’ or ‘result’ you can check – but to me, checking for the existence of an ‘error’ property is just as good, and saves a little bit of valuable bandwidth.

      Do you throw an HTTP error as well, or return it as normal?

      I think the important thing, really, is just to consider errors that may occur, and have a consistent method of returning them that can be predicted by the user.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>