Getting started with the Falcon Framework (using Python)

Post to Twitter

Today I’ll show you how to get started with Falcon. What is Falcon? Well, to quote from the GitHub repo for Falcon: “Falcon is a really fast, light-weight framework for building cloud APIs. It tries to do as little as possible while remaining highly effective“. Falcon was created by Kurt Griffiths and he is the current project maintainer. If you want to help out on the project Kurt has stated that pull requests are welcome.


Note: For these examples I’m using Falcon with Python 2.7.x. and I’ll be installing Falcon using PIP.

Update: Feb. 19, 2013 – Updated code to reflect latest changes in Falcon.

To get started I’m going to use Virtualenv to keep my dependencies organized and to keep my dev environment for this example nice and clean. I’m also going to use uWSGI to host the example.

$ virtualenv FalconExample
$ cd FalconExample
$ source bin/activate
$ pip install falcon
$ pip install uwsgi
$ touch Example1.py

Go ahead and open the Example1.py file in your favorite editor. Add the following code borrowed from the GitHub Falcon example.

import falcon

class ThingsResource(object):
    def on_get(self, req, resp):
        """Handles GET requests"""
        resp.status = falcon.HTTP_200
        resp.body = 'Hello world!'

# falcon.API instances are callable WSGI apps
wsgi_app = api = falcon.API()

# Resources are represented by long-lived class instances
things = ThingsResource()

# things will handle all requests to the '/things' URL path
api.add_route('/things', things)

Lets go ahead and test it out. From the command line type in the following command:

$ uwsgi --http :8080 --wsgi-file Example1.py --callable wsgi_app

Go to this address with your web browser: http://localhost:8080/things

The result should be the following:

Screen Shot 2013-01-23 at 7.45.48 PM

Now we will also listen for HTTP POSTs. In fact, we will listen for incoming POSTs that contain valid JSON. If the JSON is valid we will then send that JSON back to the client along with an HTTP 202 status code.

import falcon
import json

class ThingsResource(object):
    def on_get(self, req, resp):
        """Handles GET requests"""
        resp.status = falcon.HTTP_200
        resp.body = 'Hello world!'

    def on_post(self, req, resp):
        """Handles POST requests"""
        try:
            raw_json = req.stream.read()
        except Exception as ex:
            raise falcon.HTTPError(falcon.HTTP_400,
                'Error',
                ex.message)

        try:
            result_json = json.loads(raw_json, encoding='utf-8')
        except ValueError:
            raise falcon.HTTPError(falcon.HTTP_400,
                'Malformed JSON',
                'Could not decode the request body. The '
                'JSON was incorrect.')

        resp.status = falcon.HTTP_202
        resp.body = json.dumps(result_json, encoding='utf-8')

# falcon.API instances are callable WSGI apps
wsgi_app = api = falcon.API()

# Resources are represented by long-lived class instances
things = ThingsResource()

# things will handle all requests to the '/things' URL path
api.add_route('/things', things)

You can use a tool like Poster for Firefox or any other tool you want to use that can POST JSON to the endpoint: http://localhost:8080/things

Here are my settings with Poster:

Firefox Poster Settings

Here are the results when I POST to the server:

JSON results in Firefox Poster

If I use invalid JSON this is the error I get back along with an HTTP 400 status code:

Invalid JSON Error

Note: Notice how the content type coming back is already set to application/json.

I’m going to add support for reading a value on a GET request. In this case the client will send us a request for an order number that our system could then look up. The code looks like this:

import falcon

class OrdersResource(object):
    def on_get(self, req, resp, user_id):
        """Handles GET requests for orders"""
        resp.set_header('Content-Type', 'text/plain')
        resp.status = falcon.HTTP_200
        resp.body = 'You inquired about order: {0}'.format(user_id)

wsgi_app = api = falcon.API()

orders = OrdersResource()

api.add_route('/orders/{user_id}', orders)

Go ahead and test out the new resource : http://localhost:8080/orders/12345

Sending a GET request for an order number

Results:

Order request results

Combining the previous example with the original code you would have this:

import falcon
import json

class ThingsResource(object):
    def on_get(self, req, resp):
        """Handles GET requests"""
        resp.status = falcon.HTTP_200
        resp.body = 'Hello world!'

    def on_post(self, req, resp):
        """Handles POST requests"""
        try:
            raw_json = req.stream.read()
        except Exception as ex:
            raise falcon.HTTPError(falcon.HTTP_400,
                'Error',
                ex.message)

        try:
            result_json = json.loads(raw_json, encoding='utf-8')
        except ValueError:
            raise falcon.HTTPError(falcon.HTTP_400,
                'Malformed JSON',
                'Could not decode the request body. The '
                'JSON was incorrect.')

        resp.status = falcon.HTTP_202
        resp.body = json.dumps(result_json, encoding='utf-8')


class OrdersResource(object):
    def on_get(self, req, resp, user_id):
        """Handles GET requests for orders"""
        resp.set_header('Content-Type', 'text/plain')
        resp.status = falcon.HTTP_200
        resp.body = 'You inquired about order: {0}'.format(user_id)

# falcon.API instances are callable WSGI apps
wsgi_app = api = falcon.API()

# Resources are represented by long-lived class instances
things = ThingsResource()
orders = OrdersResource()

# things will handle all requests to the '/things' URL path
api.add_route('/things', things)
api.add_route('/orders/{user_id}', orders)

As you can see Falcon makes it very easy to create (Cloud) REST APIs. The project (currently) is still listed as experimental but there is already a lot of interest it and several people are committing code to the project already.

Post to Twitter

This entry was posted in Open Source, Python, REST. Bookmark the permalink.

5 Responses to Getting started with the Falcon Framework (using Python)

  1. Thanks for the write-up! Pull requests are indeed welcome. :D

  2. Jason Legler says:

    This example is great. It did fail to work with python3.3 though. I rewrote the on_post function to make it work with python3.3.


    def on_post(self, req, resp):
    """Handles POST requests"""
    try:
    raw_json = req.stream.read()
    except Exception as ex:
    raise falcon.HTTPError(falcon.HTTP_400,
    'Error',
    '{0}'.format(ex))

    try:
    result_json = json.loads(raw_json.decode('utf-8'))
    except ValueError as e:
    raise falcon.HTTPError(falcon.HTTP_400,
    'Malformed JSON',
    '{0}'.format(e))

    resp.status = falcon.HTTP_202
    resp.body = json.dumps(result_json)

  3. Chad Lung says:

    Thanks Jason!

  4. Harvey says:

    hi, could you please advise what is the best way to route static data via falcon? e.g. those css/js file referenced from the index.html? thanks

  5. Chad Lung says:

    @Harvey,

    Falcon is geared towards building cloud APIs (REST), not web based projects (HTML, JavaScript, etc.).

Comments are closed.