[Experimental]
Falcon is a really fast, light-weight framework for building cloud APIs. It tries to do as little as possible while remaining highly effective.
Perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away.
- Antoine de Saint-Exupéry
Light-weight. Only the essentials are included, with zero dependencies beyond the standard library. We work to keep the code lean-n-mean, making Falcon easier to test, optimize, and deploy.
Fast. Cloud APIs need to turn around requests quickly, and make efficient use of hardware. Falcon processes requests several times faster than other popular web frameworks.
Cloud-friendly. Falcon uses the web-friendly Python language, and speaks WSGI, so you can deploy it on your favorite stack. The framework is designed from the ground up to embrace HTTP, not work against it. Plus, diagnostics are built right in to make it easier to track down sneaky bugs and frustrating performance problems.
$ pip install falcon
$ python setup.py test
To test across all supported Python versions:
$ pip install tox && tox
More/better docs are on the way, but in the meantime, here is a simple example showing how to create a Falcon-based API.
class ThingsResource:
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)
Here is a more involved example, demonstrating getting headers and query parameters, handling errors, and reading/writing message bodies.
class ThingsResource:
def __init__(self, db):
self.db = db
self.logger = logging.getLogger('thingsapi.' + __name__)
def on_get(self, req, resp, user_id):
marker = req.get_param('marker', default='')
limit = req.get_param('limit', default=50)
# Alternatively, do this in middleware
token = req.get_header('X-Auth-Token')
if token is None:
raise falcon.HTTPUnauthorized('Auth token required',
'Please provide an auth token as '
'part of the request',
'http://docs.example.com/auth')
# Note: token_is_valid is used as an example
# and does not actually exist
if not token_is_valid(token, user_id):
raise falcon.HTTPUnauthorized('Authentication required',
'The provided auth token is not '
'valid. Please request a new token '
'and try again.',
'http://docs.example.com/auth')
# Alternatively, do this in middleware
if not req.client_accepts_json():
raise falcon.HTTPUnsupportedMediaType(
'Media Type not Supported',
'This API only supports the JSON media type.',
'http://docs.examples.com/api/json')
try:
result = self.db.get_things(marker, limit)
except Exception as ex:
self.logger.error(ex)
description = ('Aliens have attacked our base. We will be back'
'as soon as we fight them off. We appreciate your'
'patience.')
raise falcon.HTTPServiceUnavailable('Service Outage', description)
resp.set_header('X-Powered-By', 'Donuts')
resp.status = falcon.HTTP_200
resp.body = json.dumps(result)
def on_post(self, req, resp):
try:
raw_json = req.body.read()
except Exception:
raise falcon.HTTPError(falcon.HTTP_748,
'Read Error',
"Could not read the request body. I bet "
"it's them ponies again.")
try:
thing = json.loads(raw_json, 'utf-8')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_753,
'Malformed JSON',
'Could not decode the request body. The '
'JSON was incorrect.')
try:
proper_thing = self.db.add_thing(thing)
# Note: StorageError is used as an example
# and does not actually exist
except StorageError:
raise falcon.HTTPError(falcon.HTTP_725,
'Database Error',
"Sorry, couldn't write your thing to the "
'database. It worked on my machine.')
resp.status = falcon.HTTP_201
resp.set_header('Location', '/{userId}/things/' + proper_thing.id)
wsgi_app = api = falcon.API()
db = StorageEngine()
things = ThingsResource(db)
api.add_route('/{user_id}/things', things)
Pull requests are welcome. Just make sure to include tests and follow PEP 8. Commit messages should be formatted using AngularJS conventions (one-liners are OK for now but body and footer may be required as the project matures). Comments follow Google's style guide.