sesc360
sesc360

Reputation: 3255

Restful API Design Content Output

I finally got my API output ready with some great help of SO-members answering all my questions. Thank you by the way.

But I wonder about one thing in my output. When I call the URL to receive the API content I get something like this:

[
    {
    "data": [
    {
    "incidentReference": "R-20150405-887f93",
    "latitude": 48.259698,
    "longitude": 11.434679,
    "archived": false
    },
    (...)
    ]
    }
]

I read the book "Build APIs you won't hate" and it is a great resource for a lot of stuff. But I don't think, the output I see is right. I mean, the namespacing is something I would like to have. But shouldn't it look like this?

{
        "data": [
        {
        "incidentReference": "R-20150405-887f93",
        "latitude": 48.259698,
        "longitude": 11.434679,
        "archived": false
        },
        (...)
        ]
 }

So shouldn't the whole thing be JSON only? in my case it is return additionally within an Array. The functions doing the job are these:

public function index()
{
    $incidents = Incident::all();

    if( ! $incidents) {
        return Response::json([
            'error' => [
                'message' => 'There are no incidents in the database.',
                'code' => 100
            ]
        ], 404);
    } else {
        return $this->respond([
            $this->respondWithCollection($incidents, new IncidentTransformer)
        ]);
    }
}



  public function respond($data, $headers = []) {
        return Response::json($data, $this->getStatusCode(), $headers);
    }



  protected function respondWithCollection($collection, $callback) {
        $resource = new Collection($collection, $callback);
        $rootScope = $this->fractal->createData($resource);
        return $rootScope->toArray();
    }

So yes, the respondWithCollection returns an array, but this is handled within the respond function which states return Response::json So I would expect a json output when calling the resource.

Is this ok?

Upvotes: 0

Views: 85

Answers (2)

SimDion
SimDion

Reputation: 1080

RESTful responses should be as simple as possible : Output pure json data only. Therefore, limit and offset should not be included in the server response since this client already knows that information. And if you format the response, you will have to interface all device / plateform / system that wants to interact with your resful service.

Server should return extra information that client doesn't know though, such as total elements when querying a part of a collection. But I would use header for that. That way, server still returns simple json array, wich can be handled not only by your client but lots of other devices / platforms / apps.

My opinion : Output only pure json and use header for extra info, like this example :

api/incidents/index.php :

// sanitize $_GET array, then output
$total = $incidents->getTotal();
$output = json_encode($incidents->fetch($GET['LIMIT'], $_GET['OFFEST'])); // pure example, I don't know how your framework works

// output
header('HTTP/1.1 200 OK');
header('Content-Type: application/json');
header('Collection-Total: '.$total);
header('Content-Length: ' . strlen($output));
echo $output;

For example, a web app using jquery accessing this ressource would look like this :

var limit = 10;
var offset = 200;
$.ajax({
    type: 'GET',
    url:'http://mywebsite.com/api/incidents/',
    data: {
        'LIMIT': limit, 
        'OFFSET': offset
    },
    success: function(data, textStatus, request){
        console.log('Incidents received...');
        console.log(data);
        console.log('There is ' + request.xhr.getResponseHeader('Collection-Total') + ' incidents in total');
    },
});

I would avoid nested structures, like Roman said. RESTful ressources need to have their own identifier (URI). It must return an object (item) or array of objects (collection of items), like this :

api/incidents // returns an array of objects
api/incident/[id] // returns a unique object (incident), using incident id in the URI
api/incidentreference/[id] // return an unique object (incident reference), usign incident reference id in URI

This approach also leads to interesting cache possibilities since all elements (item or collections) have their own identifier (URI). Web protocols / platforms / devices may cache all server results and optimize your entire app automatically if you work with URIs.

I would also recommend that your service return a 200 OK responses even if there is no elements to output. 404 says that the ressource is not found. The ressource is found, but contains no elements now. It may contains elements later. A 404 HTTP code may be handled differently by some device / broswers / platform. I am possibily wrong with that, but my REST services always return 200 OK / empty json arrays and I never has problems. Accessing a ressource (url) that doesn't exists will return 404 though.

Upvotes: 0

Roman Battlez
Roman Battlez

Reputation: 42

The next structure

 {"data" : [{}, {}]} 

is good when you have an extra fields, such as total count of items, number of page, etc:

 {"data" : [{}, {}], "page":1, "total": 100} 

Otherwise, it is really good to use simple structure:

[{"incidentReference": "R-20150405-887f93", ...}, {...}]

I'd recommend you to avoid any deep-nested structures.

Upvotes: 1

Related Questions