I’ve been working on a little project lately using Pyramid and AngularJS, for server and client side respectively. Naturally, this means that my Angular’s Controllers need to make AJAX
calls to my server in order to get and send data. So, I have decided to create a little RESTful API to easily do this.
There are many benefits of using a RESTful web service – this IBM article does an excellent job of explaining them – and I wanted to leverage them for my project. What I wanted to do was:
GET /user - get a collection of ALL users
POST /user - create new user
GET /user/name - get the user with that 'name'
PUT /user/name - update user with that 'name'
DELETE /user/name - delete user with that 'name'
Of course, name
is a horrible key to use. I’ll probably end up using id
instead, but for the purpose of this post, name
will suffice.
In Pyramid you have the ability of mapping different request methods to the same route. For example:
from pyramid.response import Response
from pyramid.config import Configurator
from pyramid.view import view_defaults
@view_defaults(route_name='user')
class USERView(object):
def __init__(self, request):
self.request = request
@view_config(request_method='GET')
def get(self):
return Response('get')
@view_config(request_method='POST')
def post(self):
return Response('post')
@view_config(request_method='PUT')
def put(self):
return Response('put')
@view_config(request_method='DELETE')
def delete(self):
return Response('delete')
if __name__ == '__main__':
config = Configurator()
config.add_route('user', '/user')
config.scan()
...
In this example, whenever the URL /user
is visited, Pyramid will render the view that corresponds with the request_method
.Such that,
GET /user => USERView.get
POST /user => USERView.post
...
So far so good. This is just how REST
works, however there’s a little caveat. If I want to get a specific user I need to do:
myapp.com/user?name=carlos
Uh-oh. This is not how I wanted to access a user. Not only that, but it also breaks one of REST design principles: Expose directory structure-like URIs. This URI, besides not being intuitive, it’s hard to remember – therefore making for a horrible API. Instead, it should look like:
myapp.com/user/carlos
In order to get the routes to look like the above, I could do:
...
@view_defaults(route_name='user')
class USERView(object):
...
@view_config(request_method='GET')
def getUser(self):
id = self.request.matchdict['name']
...
@view_config(route_name='users', request_method='GET')
def getAllUsers(self):
...
... same methods as before ...
if __name__ == '__main__':
config = Configurator();
config.add_route('user', '/user/{name}')
config.add_route('users', '/user')
config.scan()
...
Notice how I added a new route config.add_route('users', '/user')
which will handle getting ALL of the users. I also updated the old one to handle access to a specific user config.add_route('user', '/users/{name}')
.
I added a new method to the USERView
class to handle the new /users
route. This works, because arguments passed to @view_config
decorator override those passed to @view_defaults
. Meaning that getAllUsers
overrides its class’ route and specifies its own. For more information about how @view_defaults
works, check the Pyramid documentation.
Now, our requests do the following:
GET /user/carlos => USERView.getUser
GET /user => USERView.getAllUsers
POST /user => USERView.post
Using Cornice
The fine folks at Mozilla realized that specifying multiple routes and multiple request methods every time you wanted to create a RESTful API was cumbersome. So they created a simple library to facilitate this. Enter Cornice.
from pyramid.config import Configurator
from cornice.resource import resource, view
@resource(collection_path='/user', path='/user/{name}')
class USERView(object):
def __init__(self, request):
self.request = request
def collection_get(self):
return {'users': ... }
@view(renderer='json')
def get(self):
name = self.request.matchdict['name']
....
if __name__ == '__main__':
config = Configurator()
# adds cornice
config.include("cornice")
config.scan()
...
Our requests now look like:
GET /user/carlos => USERView.get
GET /user => USERView.collection_get
POST /user => USERView.post
Voila! Cornice’s @resource(collection_path='/user', ...)
maps to collection_get
method, which is used whenever I need to get ALL of the users. While @resource(..., path='/user/{name})
maps to the corresponding get, post, put, delete
method.
With Cornice, I obtained the same output with half the amount of work. What kind of sorcery is this? Actually, Cornice’s insides are doing exactly the same thing I did above!
So, guess what ended up using for my web app? Yep, I decided to go with Cornice. Besides being easier to set up and read, it provides some robust validation features – which are out of the scope of this post – which will be useful later on during development.