Developing with REST
- 11/8/2011
- ·
- #index
The application’s released, the userbase is growing, and the company inbox is filling up with inquiries from developers curious about your data set. Maybe it’s time to start thinking about opening the API.
It’s usually a good idea to approach development with an API-first mindset, but many older applications were designed primarily to serve themselves. Even well-established applications can benefit from opening parts of their platform, however, a move that can:
- Expand the use of the application’s dataset
- Increase relevance of the application’s platform
- Enable growth through third party features and enhancments
REST provides one roadmap for exposing application data to the world. Before diving into the details, it’s worth mentioning that there are many easier ways to develop a RESTful API without building it from scratch. But for existing applications whose architecture doesn’t fit the conventions of an existing framework, a custom implementation may represent a lesser evil.
To keep the REST-zealots at bay, the journey we’re about to embark upon won’t outline a fully RESTful development approach. Call it REST-ish, if you like—80% there, with the details omitted for the sake of brevity.
A brief introduction to REST
REST is an architectural style that envisions the internet as a collection of server-based resources made available to clients at unique locations (URLs). Clients make requests to access or modify individual resources based on the resource’s location and the action to be taken. For instance, a RESTful request to access a single blog post might look something like this:
GET /blog/posts/14
If it looks a lot like an HTTP GET request, that’s because it is: in REST, an HTTP request defines the “verb” used to operate on the resource located at a specific URL. Mapped over the usual CRUD, REST typically relies on the following mappings:
Request Type | Resource | Collection | GET | Read | Read all | POST | Create | PUT | Update | Replace collection | DELETE | Delete | Delete |
---|
Beyond the verb and the resource location, the format of the request matters only in that the server must recognize and respond in the same format. In theory, a full-on implementation of REST should be able to meet any request with a response in the same tongue. In reality, “RESTful” applications rarely implement more than one or two serialization formats (XML and JSON being the common cases).
Handling Requests
In PHP, most of the details of a RESTful request end up in the $_SERVER
array. The specific fields may vary slightly depending on how the server is set up—in applications that rely on the front controller pattern, for instance, the URL of the requested resource is likely to be stored in $_SERVER['QUERY_STRING']
instead—but those relevant to request processing typically include:
Request | `$_SERVER` field | Notes | Resource | `REQUEST_URI` | See below | Type | `REQUEST_METHOD` | Format | `CONTENT_TYPE` | PUT and POST only |
---|
$method = $_SERVER[ 'REQUEST_METHOD' ];
if( $method == 'POST' || $method == 'PUT' ) {
$request = file_get_contents( 'php://input' );
}
The $request
retrieved from the input buffer is just a string containing a serialized representation of the PUSHed or POSTed resource. Fortunately, the mime-type specified by $_SERVER['CONTENT_TYPE']
offers some clue over to the format that was used to encode it—information that a listening service can use to translate the request string into a native PHP object.
$method = $_SERVER[ 'REQUEST_METHOD' ];
if( $method == 'POST' || $method == 'PUT' ) {
$request = file_get_contents( 'php://input' );
$format = $_SERVER[ 'CONTENT_TYPE' ];
switch( $format ) {
case 'application/json':
// decode JSON request
$request = json_decode( $request );
break;
default:
// unknown format
}
}
From here, request processing should pass to some kind of handler function. There are a several ways to do this, and the “right” approach will depend on the style of the application in question. One common pattern, however, passes execution to a handler class with get()
, post()
, update()
, and delete()
methods mapped as callbacks to the appropriate REST methods.
Sending responses
Once a request has been received and translated, the application will need to generate an appropriate response. A RESTful response has three parts:
// 1. A status code describing the success or failure of the request
header( 'HTTP/1.0 200 OK' );
// 2. A content-header affirming the format of the response
header( 'content-type: application/json' );
// 3. The (appropriately encoded) body of the response
echo json_encode( $response );
exit();
REST relies on HTTP status codes to determine whether a request was successful. Though it probably makes better sense to create methods for each status result, an index of statuses can illustrate the point:
$code = 200;
$descriptions = array(
200 => 'OK',
400 => 'bad request',
404 => 'not found',
500 => 'internal server error',
// ...
);
header( "HTTP/1.0 $code {$descriptions[$code]}" );
The content-type
is easy—nine times out of ten, it should match the $format
of the request—and the response body is just a matter of ensuring that the resulting resource is serialized appropriately. The same mime-type switch
previously employed to decode the request format can now be used to convert a native object ($response
) into the appropriate format.
switch( $format ) {
case 'application/json':
// encode response
$response = json_encode( $response );
break;
}
Putting it all together, a generic response
method might look something like this:
public function response( $code, $format, $response ) {
$descriptions = array(
200 => 'OK',
400 => 'bad request',
404 => 'not found',
500 => 'internal server error',
// ...
);
header( "HTTP/1.0 $code {$descriptions[$code]}" );
header( 'content-type: ' . $format );
switch( $format ) {
case 'application/json':
$response = json_encode( $response );
break;
}
echo $response;
exit();
}
Conclusion
REST is just one architecture among many that a development team might select to expose an application, but its close relationship with HTTP and simple structure make it a particularly common one. A full-blown API will be considerably more involved than the coarse interactions outlined here, of course, but it will also likely take advantage of one of the many excellent REST implementations available for the development team’s language of choice. Check ‘em out!