Writing REST web services with Dancer

It's really easy to write REST web services with Dancer.

A REST application is defined by:

  • a route to a resource
  • an HTTP verb
  • a format

Serializers

Dancer comes with some helper functions, in order to help you to serialize your Perl data structure to something like JSON or XML.

The core provides serializers for:

  • JSON (to_json and from_json)
  • XML (to_xml and from_xml)
  • YAML (to_yaml and from_yaml)

With these serializers, you can write:

get '/user/:id' => sub {
    my $user = $schema->resultset('Users')->find( params->{id} );
    content_type 'application/json';
    return to_json { id => params->{id}, name => $user->name };
};

Let Dancer handle the serialization

Using JSON

Now that we've seen how to do serialization manually, lets make Dancer do the work for us.

You can configure your application to have a default serializer:

set serializer => 'JSON';

Now, you can define a route like this:

get '/user/:id' => sub {
    my $id   = params->{id};
    my $user = $schema->resultset('Users')->find($id);
    return { id => $id, name => $user->name };
};

As you can see, we no longer need to call the content_type and to_json functions. When you set a default serializer and your route returns an arrayref or hashref, Dancer will check if you have a default serializer defined. If that is the case, it will serialize the response to the appropriate format and set the correct content type.

Let the user select the format.

If you have installed the required dependencies for using all the supported serializers by Dancer (XML::Simple, YAML and JSON), you can even let the user choose in which format he prefers to have the content of the request. For this, you'll need to set the serializer to mutable (Dancer::Serializer::Mutable. From now on, you can select your format by defining the correct value in the HTTP header of your request (Content-Type when doing a POST or PUT operation, and Accept-Type for GET and DELETE).

So, if we take our previous example, by setting the serializer to mutable, we can do the following:

$ curl http://localhost:5000/1
{"name":"franck","id":"1"}

$ curl -H 'Accept-Type: text/x-yaml' http://localhost:5000/1
---
id: 1
name: franck

$ curl -H 'Accept-Type: text/xml' http://localhost:5000/1
<data name="franck" id="1" />

Dancer::Plugin::REST

There is an excellent plugin available to help you to write a REST application: Dancer::Plugin::REST. With this one, you'll be able to declare routes to access resources, but also declare how you want to handle the format.

Format

As we have seen, you can already define the format by setting the serializer option to a certain value (JSON, YAML, mutable, etc.). The plugin add another way: by appending the format to your request. This mean you can declare this kind of route:

prepare_serializer_for_format;

get '/user/:id.:format' => sub {
    my $user = $schema->resultset('Users')->find(params->{id});
    return { id => params->{id}, name => $user->name };
};

$ curl http://localhost:5000/1.json
{"name":"franck","id":"1"}

$ curl -H 'Accept-Type: text/xml' http://localhost:5000/1.xml
<data name="franck" id="1" />

Resources

As stated at the beginning of the article, with REST you defined routes to access resources. So let's do this:

resource user => 
    'get'    => \&on_get_user,
    'create' => \&on_create_user,
    'delete' => \&on_delete_user,
    'update' => \&on_update_user;

sub on_get_user {
    ...
    status_ok( { user => $users->id } );
}

sub on_create_user {
    ...
    status_created( { user => $user->id } );
}

...

Helpers

The plugin add also some helpers to return an appropriate HTTP status to your request.

post '/user/' => sub {
    my $user = $schema->resultset('Users')->create(...);
    status_created( { id => $user->id, name => $user->name } );
};

This will return a new HTTP response with a status of 201.

Credits

Most of the work done on the serializers have been inspired by the excellent plugin Catalyst::Action::REST. Thanks to all of the authors of this module.

Author

This article has been written by Franck Cuny for the Perl Dancer Advent Calendar 2010.

Copyright

Copyright (C) 2010 by Franck Cuny <franck@lumberjaph.net>.