Skip to content
mjumbewu edited this page Aug 29, 2011 · 7 revisions

RESTful API

In order to facilitate cleaner code on the front end, we have started providing access to the database models through a RESTful API. There's a good three-part series [1] introducing REST for more information.

The endpoints are all prefixed with /rest/vX where X is the version number for the REST interface. The most recent version is 1 (since we just started).

Resources

The available resources are:

Resource Description
/rest/v1/needs/ The collection of needs. Use query parameters to access a collection of needs for a particular project. Anyone may read any needs, but only project administrators or site administrators may create new needs.
/rest/v1/needs/ID A particular need identified by the key ID. Anyone may read any need, but only project administrators or site administrators may update or delete a need.
/rest/v1/needs/ID/volunteers/ The collection of volunteers for a given need. This may only be used to create a new volunteer at the moment. Only project members can create a new volunteer for themselves.

Making Requests

In general, for a given model, the endpoints are listed below. The HTTP method may either be specified as described; alternatively, for PUT and DELETE, send a POST request with an additional parameter _method

A collection resource for a given model can be accessed at the /model/ path. It represents the collection of model objects in the system. Generally, you may either retrieve the collection (or a subset), or create a new item in the collection. Upon success, this resource will give a 200 OK response.

  • GET /model/

    Get a list of model objects. The response will contain a representation of the identified model objects.

  • POST /model/

    Creates a new model object within the collection. The response will contain a representation of the created model object.

An instance resource for a given model can be accessed at the /model/ID path. It represents a particular model object instance identified by ID. Generally, you may retrieve details about the resource, make changes to the resource, or delete the resource. Upon success, this resource will return a 200 OK status. If an instance identified by ID does not exist, the resource will return a 404 Not Found response. If multiple resources exist identified by ID, the resource will return a 409 Conflict response.

  • GET /model/ID

    Gets a resource identified by ID. The response will contain a representation of the identified model object.

  • PUT /model/ID

    Modify the resource identified by ID. The response will contain an updated representation of the identified resource object.

  • DELETE /model/ID

    Delete the resource identified by ID. The response will be empty.

All resources will return the following status codes on error:

  • 403 Forbidden if the current user does not have permission to perform the requested action
  • 405 Method not allowed if an HTTP request method is supplied to a resource that does not support that method (e.g. PUT for a collection).

Implementation

Creating a New Resource Endpoint

Say you want to create resources based around projects (and collections of projects). Import the controllers.rest module, and the giveaminute.models module:

from controllers import rest
from giveaminute import models

Assuming we have a model called models.Project already (which we do), we can begin to make our resources. Say we want one resource for project collectir ons. We want to be able to retrieve collections and add new projects to them. We can construct the resource using a RestController:

class ProjectCollectionResource (rest.ListInstancesMixin,
                                 rest.CreateInstanceMixin,
                                 rest.RestController):
    model = models.Project

We use two mixins here to give the resource controller the ability to list and create resources. The other mixins available from the controllers.rest module are ReadInstanceMixin, UpdateInstanceMixin, and DeleteInstanceMixin. Note that in the default implementation, the ReadInstanceMixin and ListInstancesMixin are mutually exclusive, as they will both be blindly mapped to the GET method; you'll only ever get one behavior.

After you have created your controller, in the main module add a couple of routes identifying your resources:

ROUTES = (...

    '^/rest/v1/projects/$',
    'controllers.rest.ProjectCollectionResource',

    '^/rest/v1/projects/(\d+)/$,
    'controllers.rest.ProjectInstanceResource',

    ...)

That's it!

Restricting Permission on Your Resource

By default a resource has full read-only permission restrictions. If you provide a CreateInstanceMixin or a DeleteInstanceMixin, then no one will be able to create or delete instances. Safe, but not very useful. Use rest.ResourceAccessRules objects to differently allow and restrict access. For example, if you want your project resources to be read-only for everyone except administrative users, use the rest.NonAdminReadOnly access rules:

class ProjectCollectionResource (rest.ListInstancesMixin,
                                 rest.CreateInstanceMixin,
                                 rest.RestController):
    model = models.Project
    access_rules = rest.NonAdminReadOnly()

ResourceAccessRules classes implement permission checkers that operate on an instance of a user model (or None) and an arbitrary resource instance. The permission checking methods are:

  • can_read(user, instance)

    Returns True if the user can read the resource

  • can_create(user, instance)

    Returns True if the user can store the resource

  • can_update(user, instance)

    Returns True if the user can modify the resource

  • can_delete(user, instance)

    Returns True if the user can delete the resource

These methods can be overridden in your own access rules class to create a custom rule system.


[1]

A three-part series introducing REST:

  1. A Brief Introduction to REST
  2. Addressing Doubts about REST
  3. REST Anti-Patterns