FAQ

Design Philosophy

Why doesn’t Falcon come with batteries included?

Falcon is designed for applications that require a high level of customization or performance tuning. The framework’s minimalist design frees the developer to select the best strategies and 3rd-party packages for the task at hand.

The Python ecosystem offers a number of great packages that you can use from within your responders, hooks, and middleware components. As a starting point, the community maintains a list of Falcon add-ons and complementary packages.

Why doesn’t Falcon create a new Resource instance for every request?

Falcon generally tries to minimize the number of objects that it instantiates. It does this for two reasons: first, to avoid the expense of creating the object, and second to reduce memory usage by reducing the total number of objects required under highly concurrent workloads. Therefore, when adding a route, Falcon requires an instance of your resource class, rather than the class type. That same instance will be used to serve all requests coming in on that route.

What happens if my responder raises an error?

Generally speaking, Falcon assumes that resource responders (such as on_get(), on_post(), etc.) will, for the most part, do the right thing. In other words, Falcon doesn’t try very hard to protect responder code from itself.

Note

As of version 3.0, the framework will no longer propagate uncaught exceptions to the application server. Instead, the default Exception handler will return an HTTP 500 response and log details of the exception to wsgi.errors.

Although providing basic error handlers, Falcon optimizes for the most common case where resource responders do not raise any errors for valid requests. With that in mind, writing a high-quality API based on Falcon requires that:

  1. Resource responders set response variables to sane values.

  2. Your code is well-tested, with high code coverage.

  3. Errors are anticipated, detected, and handled appropriately within each responder and with the aid of custom error handlers.

How do I generate API documentation for my Falcon API?

When it comes to API documentation, some developers prefer to use the API implementation as the user contract or source of truth (taking an implementation-first approach), while other developers prefer to use the API spec itself as the contract, implementing and testing the API against that spec (taking a design-first approach).

At the risk of erring on the side of flexibility, Falcon does not provide API spec support out of the box. However, there are several community projects available in this vein. Our Add on Catalog lists a couple of these projects, but you may also wish to search PyPI for additional packages.

If you are interested in the design-first approach mentioned above, you may also want to check out API design and gateway services such as Tyk, Apiary, Amazon API Gateway, or Google Cloud Endpoints.

Performance

Does Falcon work with HTTP/2?

Falcon is a WSGI framework and as such does not serve HTTP requests directly. However, you can get most of the benefits of HTTP/2 by simply deploying any HTTP/2-compliant web server or load balancer in front of your app to translate between HTTP/2 and HTTP/1.1. Eventually we expect that Python web servers (such as uWSGI) will support HTTP/2 natively, eliminating the need for a translation layer.

Is Falcon thread-safe?

The Falcon framework is, itself, thread-safe. For example, new Request and Response objects are created for each incoming HTTP request. However, a single instance of each resource class attached to a route is shared among all requests. Middleware objects and other types of hooks, such as custom error handlers, are likewise shared. Therefore, as long as you implement these classes and callables in a thread-safe manner, and ensure that any third-party libraries used by your app are also thread-safe, your WSGI app as a whole will be thread-safe.

Does Falcon support asyncio?

Starting with version 3.0, the ASGI flavor of Falcon now proudly supports asyncio! Use the falcon.asgi.App class to create an async application, and serve it via an ASGI application server such as Uvicorn.

Alternatively, IO-bound WSGI applications can be scaled using the battle-tested gevent library via Gunicorn or uWSGI. meinheld has also been used successfully by the community to power high-throughput, low-latency WSGI services.

Tip

Note that if you use Gunicorn, you can combine gevent and PyPy to achieve an impressive level of performance. (Unfortunately, uWSGI does not yet support using gevent and PyPy together.)

Does Falcon support WebSocket?

The async flavor of Falcon supports the ASGI WebSocket protocol. See also: WebSocket (ASGI Only).

WSGI applications might try leveraging uWSGI’s native WebSocket support or gevent-websocket’s GeventWebSocketWorker for Gunicorn.

As an option, it may make sense to design WebSocket support as a separate service due to very different performance characteristics and interaction patterns, compared to a regular RESTful API. In addition to (obviously!) Falcon’s native ASGI support, a standalone WebSocket service could also be implemented via Aymeric Augustin’s handy websockets library.

Routing

How do I implement CORS with Falcon?

In order for a website or SPA to access an API hosted under a different domain name, that API must implement Cross-Origin Resource Sharing (CORS). For a public API, implementing CORS in Falcon can be as simple as passing the cors_enable flag (set to True) when instantiating your application.

Further CORS customization is possible via CORSMiddleware (for more information on managing CORS in Falcon, see also CORS).

For even more sophisticated use cases, have a look at Falcon add-ons from the community, such as falcon-cors, or try one of the generic WSGI CORS libraries available on PyPI. If you use an API gateway, you might also look into what CORS functionality it provides at that level.

Why is my request with authorization blocked despite cors_enable?

When you are making a cross-origin request from the browser (or another HTTP client verifying CORS policy), and the request is authenticated using the Authorization header, the browser adds authorization to Access-Control-Request-Headers in the preflight (OPTIONS) request, however, the actual authorization credentials are omitted at this stage.

If your request authentication/authorization is performed in a middleware component which rejects requests lacking authorization credentials by raising an instance of HTTPUnauthorized (or rendering a 4XX response in another way), a common pitfall is that even an OPTIONS request (which is lacking authorization as per the above explanation) yields an error in this manner. As a result of the failed preflight, the browser chooses not proceed with the main request.

If you have implemented the authorization middleware yourself, you can simply let OPTIONS pass through:

class MyAuthMiddleware:
    def process_request(self, req, resp):
        # NOTE: Do not authenticate OPTIONS requests.
        if req.method == 'OPTIONS':
            return

        # -- snip --

        # My authorization logic...

Alternatively, if the middleware comes from a third-party library, it may be more practical to subclass it:

class CORSAwareMiddleware(SomeAuthMiddleware):
    def process_request(self, req, resp):
        # NOTE: Do not authenticate OPTIONS requests.
        if req.method != 'OPTIONS':
            super().process_request(req, resp)

In the case middleware in question instead hooks into process_resource(), you can use a similar treatment.

If you tried the above, and you still suspect the problem lies within Falcon’s CORS middleware, it might be a bug! Let us know so we can help.

How do I implement redirects within Falcon?

Falcon provides a number of exception classes that can be raised to redirect the client to a different location (see also Redirection).

Note, however, that it is more efficient to handle permanent redirects directly with your web server, if possible, rather than placing additional load on your app for such requests.

How do I split requests between my original app and the part I migrated to Falcon?

It is common to carve out a portion of an app and reimplement it in Falcon to boost performance where it is most needed.

If you have access to your load balancer or reverse proxy configuration, we recommend setting up path or subdomain-based rules to split requests between your original implementation and the parts that have been migrated to Falcon (e.g., by adding an additional location directive to your NGINX config).

If the above approach isn’t an option for your deployment, you can implement a simple WSGI wrapper that does the same thing:

def application(environ, start_response):
    try:
        # NOTE(kgriffs): Prefer the host header; the web server
        # isn't supposed to mess with it, so it should be what
        # the client actually sent.
        host = environ['HTTP_HOST']
    except KeyError:
        # NOTE(kgriffs): According to PEP-3333, this header
        # will always be present.
        host = environ['SERVER_NAME']

    if host.startswith('api.'):
        return falcon_app(environ, start_response)
    elif:
        return webapp2_app(environ, start_response)

See also PEP 3333 for a complete list of the variables that are provided via environ.

How do I implement both POSTing and GETing items for the same resource?

Suppose you have the following routes:

# Resource Collection
GET /resources{?marker, limit}
POST /resources

# Resource Item
GET /resources/{id}
PATCH /resources/{id}
DELETE /resources/{id}

You can implement this sort of API by simply using two Python classes, one to represent a single resource, and another to represent the collection of said resources. It is common to place both classes in the same module (see also this section of the tutorial.)

Alternatively, you can use suffixed responders to map both routes to the same resource class:

class MyResource:
    def on_get(self, req, resp, id):
        pass

    def on_patch(self, req, resp, id):
        pass

    def on_delete(self, req, resp, id):
        pass

    def on_get_collection(self, req, resp):
        pass

    def on_post_collection(self, req, resp):
        pass


# -- snip --


resource = MyResource()
app.add_route('/resources/{id}', resource)
app.add_route('/resources', resource, suffix='collection')

Why is my URL with percent-encoded forward slashes (%2F) routed incorrectly?

This is an unfortunate artifact of the WSGI specification, which offers no standard means of accessing the “raw” request URL. According to PEP 3333, the recommended way to reconstruct a request’s URL path is using the PATH_INFO CGI variable, which is already presented percent-decoded, effectively making originally percent-encoded forward slashes (%2F) indistinguishable from others passed verbatim (and intended to separate URI fields).

Although not standardized, some WSGI servers provide the raw URL as a non-standard extension; for instance, Gunicorn exposes it as RAW_URI, uWSGI calls it REQUEST_URI, etc. You can implement a WSGI (or ASGI, see the discussion below) middleware component to overwrite the request path with the path component of the raw URL, see more in the following recipe: Decoding Raw URL Path.

In contrast to WSGI, the ASGI specification does define a standard connection HTTP scope variable name (raw_path) for the unmodified HTTP path. However, it is not mandatory, and some applications servers may be unable to provide it. Nevertheless, we are exploring the possibility of adding an optional feature to use this raw path for routing in the ASGI flavor of the framework.

Extensibility

How do I use WSGI middleware with Falcon?

Instances of falcon.App are first-class WSGI apps, so you can use the standard pattern outlined in PEP-3333. In your main “app” file, you would simply wrap your api instance with a middleware app. For example:

import my_restful_service
import some_middleware

app = some_middleware.DoSomethingFancy(my_restful_service.app)

See also the WSGI middleware example given in PEP-3333.

How can I pass data from a hook to a responder, and between hooks?

You can inject extra responder kwargs from a hook by adding them to the params dict passed into the hook. You can also set custom attributes on the req.context object, as a way of passing contextual information around:

def authorize(req, resp, resource, params):
    # TODO: Check authentication/authorization

    # -- snip --

    req.context.role = 'root'
    req.context.scopes = ('storage', 'things')
    req.context.uid = 0

# -- snip --

@falcon.before(authorize)
def on_post(self, req, resp):
    pass

How can I write a custom handler for 404 and 500 pages in falcon?

When a route can not be found for an incoming request, Falcon uses a default responder that simply raises an instance of HTTPRouteNotFound, which the framework will in turn render as a 404 response. You can use falcon.App.add_error_handler() to override the default handler for this exception type (or for its parent type, HTTPNotFound). Alternatively, you may be able to configure your web server to transform the response for you (e.g., using nginx’s error_page directive).

By default, non-system-exiting exceptions that do not inherit from HTTPError or HTTPStatus are handled by Falcon with a plain HTTP 500 error. To provide your own 500 logic, you can add a custom error handler for Python’s base Exception type. This will not affect the default handlers for HTTPError and HTTPStatus.

See Error Handling and the falcon.App.add_error_handler() docs for more details.

Request Handling

How do I authenticate requests?

Hooks and middleware components can be used together to authenticate and authorize requests. For example, a middleware component could be used to parse incoming credentials and place the results in req.context. Downstream components or hooks could then use this information to authorize the request, taking into account the user’s role and the requested resource.

Why does req.stream.read() hang for certain requests?

This behavior is an unfortunate artifact of the request body mechanics not being fully defined by the WSGI spec (PEP-3333). This is discussed in the reference documentation for stream, and a workaround is provided in the form of bounded_stream.

How does Falcon handle a trailing slash in the request path?

If your app sets strip_url_path_trailing_slash to True, Falcon will normalize incoming URI paths to simplify later processing and improve the predictability of application logic. This can be helpful when implementing a REST API schema that does not interpret a trailing slash character as referring to the name of an implicit sub-resource, as traditionally used by websites to reference index pages.

For example, with this option enabled, adding a route for '/foo/bar' implicitly adds a route for '/foo/bar/'. In other words, requests coming in for either path will be sent to the same resource.

Warning

If strip_url_path_trailing_slash is enabled, adding a route with a trailing slash will effectively make it unreachable from normal routing (theoretically, it may still be matched by rewriting the request path in middleware).

In this case, routes should be added without a trailing slash (obviously except the root path '/'), such as '/foo/bar' in the example above.

Note

Starting with version 2.0, the default for the strip_url_path_trailing_slash request option changed from True to False.

Why is my query parameter missing from the req object?

If a query param does not have a value and the keep_blank_qs_values request option is set to False (the default as of Falcon 2.0+ is True), Falcon will ignore that parameter. For example, passing 'foo' or 'foo=' will result in the parameter being ignored.

If you would like to recognize such parameters, the keep_blank_qs_values request option should be set to True (or simply kept at its default value in Falcon 2.0+). Request options are set globally for each instance of falcon.App via the req_options property. For example:

app.req_options.keep_blank_qs_values = True

Why are ‘+’ characters in my params being converted to spaces?

The + character is often used instead of %20 to represent spaces in query string params, due to the historical conflation of form parameter encoding (application/x-www-form-urlencoded) and URI percent-encoding. Therefore, Falcon, converts + to a space when decoding strings.

To work around this, RFC 3986 specifies + as a reserved character, and recommends percent-encoding any such characters when their literal value is desired (%2B in the case of +).

How can I access POSTed form params?

By default, Falcon does not consume request bodies. However, a media handler for the application/x-www-form-urlencoded content type is installed by default, thus making the POSTed form available as Request.media with zero configuration:

import falcon


class MyResource:
    def on_post(self, req, resp):
        # TODO: Handle the submitted URL-encoded form
        form = req.media

        # NOTE: Falcon chooses the right media handler automatically, but
        #   if we wanted to differentiate from, for instance, JSON, we
        #   could check whether req.content_type == falcon.MEDIA_URLENCODED
        #   or use mimeparse to implement more sophisticated logic.

Note

In prior versions of Falcon, a POSTed URL-encoded form could be automatically consumed and merged into params by setting the auto_parse_form_urlencoded option to True. This behavior is still supported in the Falcon 3.x series. However, it has been deprecated in favor of URLEncodedFormHandler, and the option to merge URL-encoded form data into params may be removed in a future release.

POSTed form parameters may also be read directly from stream and parsed via falcon.uri.parse_query_string() or urllib.parse.parse_qs().

How can I access POSTed files?

If files are POSTed as part of a multipart form, the default MultipartFormHandler can be used to efficiently parse the submitted multipart/form-data request media by iterating over the multipart body parts:

for part in req.media:
    # TODO: Do something with the body part
    pass

How can I save POSTed files (from a multipart form) directly to AWS S3?

As highlighted in the previous answer dealing with files posted as multipart form, falcon.media.MultipartFormHandler may be used to iterate over the uploaded multipart body parts.

The stream of a body part is a file-like object implementing the read() method, making it compatible with boto3's upload_fileobj:

import boto3

# -- snip --

s3 = boto3.client('s3')

for part in req.media:
    if part.name == 'myfile':
        s3.upload_fileobj(part.stream, 'mybucket', 'mykey')

Note

Falcon is not endorsing any particular cloud service provider, and AWS S3 and boto3 are referenced here just as a popular example. The same pattern can be applied to any storage API that supports streaming directly from a file-like object.

How do I parse a nested multipart form?

Falcon does not offer official support for parsing nested multipart forms (i.e., where multiple files for a single field are transmitted using a nested multipart/mixed part) at this time. The usage is considered deprecated according to the living HTML5 standard and RFC 7578, Section 4.3.

Tip

If your app absolutely must deal with such legacy forms, the parser may actually be capable of the task. See more in this recipe: Parsing Nested Multipart Forms.

How do I retrieve a JSON value from the query string?

To retrieve a JSON-encoded value from the query string, Falcon provides the get_param_as_json() method, an example of which is given below:

import falcon


class LocationResource:

    def on_get(self, req, resp):
        places = {
            'Chandigarh, India': {
                'lat': 30.692781,
                'long': 76.740875
            },

            'Ontario, Canada': {
                'lat': 43.539814,
                'long': -80.246094
            }
        }

        coordinates = req.get_param_as_json('place')

        place = None
        for (key, value) in places.items():
            if coordinates == value:
                place = key
                break

        resp.media = {
            'place': place
        }


app = falcon.App()
app.add_route('/locations', LocationResource())

In the example above, LocationResource expects a query string containing a JSON-encoded value named 'place'. This value can be fetched and decoded from JSON in a single step with the get_param_as_json() method. Given a request URL such as:

/locations?place={"lat":43.539814,"long":-80.246094}

The coordinates variable will be set to a dict as expected.

By default, the auto_parse_qs_csv option is set to False. The example above assumes this default.

On the other hand, when auto_parse_qs_csv is set to True, Falcon treats commas in a query string as literal characters delimiting a comma-separated list. For example, given the query string ?c=1,2,3, Falcon will add this to your request.params dictionary as {'c': ['1', '2', '3']}. If you attempt to use JSON in the value of the query string, for example ?c={"a":1,"b":2}, the value will be added to request.params in an unexpected way: {'c': ['{"a":1', '"b":2}']}.

Commas are a reserved character that can be escaped according to RFC 3986 - 2.2. Reserved Characters, so one possible solution is to percent encode any commas that appear in your JSON query string.

The other option is to leave auto_parse_qs_csv disabled and simply use JSON array syntax in lieu of CSV.

When auto_parse_qs_csv is not enabled, the value of the query string ?c={"a":1,"b":2} will be added to the req.params dictionary as {'c': '{"a":1,"b":2}'}. This lets you consume JSON whether or not the client chooses to percent-encode commas in the request. In this case, you can retrieve the raw JSON string via get_param(), or use the get_param_as_json() convenience method as demonstrated above.

How can I handle forward slashes within a route template field?

In Falcon 1.3 we shipped initial support for field converters. We’ve discussed building on this feature to support consuming multiple path segments ala Flask. This work is currently planned to commence after the 3.0 release.

In the meantime, you can work around the issue by implementing a Falcon middleware component to rewrite the path before it is routed. If you control the clients, you can percent-encode forward slashes inside the field in question, however, note that pre-processing is unavoidable in order to access the raw encoded URI too. See also: Why is my URL with percent-encoded forward slashes (%2F) routed incorrectly?

How do I adapt my code to default context type changes in Falcon 2.0?

The default request/response context type has been changed from dict to a bare class in Falcon 2.0. Instead of setting dictionary items, you can now simply set attributes on the object:

# Before Falcon 2.0
req.context['cache_backend'] = MyUltraFastCache.connect()

# Falcon 2.0
req.context.cache_backend = MyUltraFastCache.connect()

The new default context type emulates a dict-like mapping interface in a way that context attributes are linked to dict items, i.e. setting an object attribute also sets the corresponding dict item, and vice versa. As a result, existing code will largely work unmodified with Falcon 2.0. Nevertheless, it is recommended to migrate to the new interface as outlined above since the dict-like mapping interface may be removed from the context type in a future release.

Warning

If you need to mix-and-match both approaches under migration, beware that setting attributes such as items or values would obviously shadow the corresponding mapping interface functions.

If an existing project is making extensive use of dictionary contexts, the type can be explicitly overridden back to dict by employing custom request/response types:

class RequestWithDictContext(falcon.Request):
    context_type = dict

class ResponseWithDictContext(falcon.Response):
    context_type = dict

# -- snip --

app = falcon.App(request_type=RequestWithDictContext,
                 response_type=ResponseWithDictContext)

Response Handling

When would I use media, data, text, and stream?

These four attributes are mutually exclusive, you should only set one when defining your response.

resp.media is used when you want to use the Falcon serialization mechanism. Just assign data to the attribute and Falcon will take care of the rest.

class MyResource:
    def on_get(self, req, resp):
        resp.media = {'hello': 'World'}

resp.text and resp.data are very similar, they both allow you to set the body of the response. The difference being, text takes a string, and data takes bytes.

class MyResource:
    def on_get(self, req, resp):
        resp.text = json.dumps({'hello': 'World'})

    def on_post(self, req, resp):
        resp.data = b'{"hello": "World"}'

resp.stream allows you to set a generator that yields bytes, or a file-like object with a read() method that returns bytes. In the case of a file-like object, the framework will call read() until the stream is exhausted.

class MyResource:
    def on_get(self, req, resp):
        resp.stream = open('myfile.json', mode='rb')

See also the Outputting CSV Files recipe for an example of using resp.stream with a generator.

How can I use resp.media with types like datetime?

The default JSON handler for resp.media only supports the objects and types listed in the table documented under json.JSONEncoder.

To handle additional types in JSON, you can either serialize them beforehand, or create a custom JSON media handler that sets the default param for json.dumps(). When deserializing an incoming request body, you may also wish to implement object_hook for json.loads(). Note, however, that setting the default or object_hook params can negatively impact the performance of (de)serialization.

If you use an alternative JSON library, you might also look whether it provides support for additional data types. For instance, the popular orjson opts to automatically serialize dataclasses, enums, datetime objects, etc.

Furthermore, different Internet media types such as YAML, msgpack, etc might support more data types than JSON, either as part of the respective (de)serialization format, or via custom type extensions.

See also

See Custom JSON encoder for an example on how to use a custom json encoder.

Does Falcon set Content-Length or do I need to do that explicitly?

Falcon will try to do this for you, based on the value of resp.text, resp.data or resp.media (whichever is set in the response, checked in that order).

For dynamically-generated content, you can choose to not set content_length, in which case Falcon will then leave off the Content-Length header, and hopefully your WSGI server will do the Right Thing™ (assuming you’ve told the server to enable keep-alive, it may choose to use chunked encoding).

Note

PEP-3333 prohibits apps from setting hop-by-hop headers itself, such as Transfer-Encoding.

Similar to WSGI, the ASGI HTTP connection scope specification states that responses without Content-Length “may be chunked as the server sees fit”.

Why is an empty response body returned when I raise an instance of HTTPError?

Falcon attempts to serialize the HTTPError instance using its to_json() or to_xml() methods, according to the Accept header in the request. If neither JSON nor XML is acceptable, no response body will be generated. You can override this behavior if needed via set_error_serializer().

I’m setting a response body, but it isn’t getting returned. What’s going on?

Falcon skips processing the response body when, according to the HTTP spec, no body should be returned. If the client sends a HEAD request, the framework will always return an empty body. Falcon will also return an empty body whenever the response status is any of the following:

falcon.HTTP_100
falcon.HTTP_204
falcon.HTTP_416
falcon.HTTP_304

If you have another case where the body isn’t being returned, it’s probably a bug! Let us know so we can help.

How can I serve a downloadable file with Falcon?

In the on_get() responder method for the resource, you can tell the user agent to download the file by setting the Content-Disposition header. Falcon includes the downloadable_as property to make this easy:

resp.downloadable_as = 'report.pdf'

See also the Outputting CSV Files recipe for a more involved example of dynamically generated downloadable content.

Why is Falcon changing my header names to lowercase?

Falcon always lowercases header names before storing them in the internal Response structures in order to make the response header handling straightforward and performant, as header name lookup can be done using a simple dict. Since HTTP headers are case insensitive, this optimization should normally not affect your API consumers.

In the unlikely case you absolutely must deal with non-conformant HTTP clients expecting a specific header name capitalization, see this recipe how to override header names using generic WSGI middleware: Capitalizing Response Header Names.

Note that this question only applies to the WSGI flavor of Falcon. The ASGI HTTP scope specification requires HTTP header names to be lowercased.

Furthermore, the HTTP2 standard also mandates that header field names MUST be converted to lowercase (see RFC 7540, Section 8.1.2).

Can Falcon serve static files?

Falcon makes it easy to efficiently serve static files by simply assigning an open file to resp.stream as demonstrated in the tutorial. You can also serve an entire directory of files via falcon.App.add_static_route(). However, if possible, it is best to serve static files directly from a web server like Nginx, or from a CDN.

Misc.

How do I manage my database connections?

Assuming your database library manages its own connection pool, all you need to do is initialize the client and pass an instance of it into your resource classes. For example, using SQLAlchemy Core:

engine = create_engine('sqlite:///:memory:')
resource = SomeResource(engine)

Then, within SomeResource:

# Read from the DB
with self._engine.connect() as connection:
    result = connection.execute(some_table.select())
for row in result:
    # TODO: Do something with each row

result.close()

# -- snip --

# Write to the DB within a transaction
with self._engine.begin() as connection:
    r1 = connection.execute(some_table.select())

    # -- snip --

    connection.execute(
        some_table.insert(),
        col1=7,
        col2='this is some data'
    )

When using a data access layer, simply pass the engine into your data access objects instead. See also this sample Falcon project that demonstrates using an ORM with Falcon.

You can also create a middleware component to automatically check out database connections for each request, but this can make it harder to track down errors, or to tune for the needs of individual requests.

If you need to transparently handle reconnecting after an error, or for other use cases that may not be supported by your client library, simply encapsulate the client library within a management class that handles all the tricky bits, and pass that around instead.

If you are interested in the middleware approach, the falcon-sqla library can be used to automatically check out and close SQLAlchemy connections that way (although it also supports the explicit context manager pattern).

How do I manage my database connections with ASGI?

This example is similar to the above one, but it uses ASGI lifecycle hooks to set up a connection pool, and to dispose it at the end of the application. The example uses psycopg to connect to a PostgreSQL database, but a similar pattern may be adapted to other asynchronous database libraries.

import psycopg_pool

url = 'postgresql://scott:tiger@127.0.0.1:5432/test'

class AsyncPoolMiddleware:
    def __init__(self):
        self._pool = None

    async def process_startup(self, scope, event):
        self._pool = psycopg_pool.AsyncConnectionPool(url)
        await self._pool.wait()  # created the pooled connections

    async def process_shutdown(self, scope, event):
        if self._pool:
            await self._pool.close()

    async def process_request(self, req, resp):
        req.context.pool = self._pool

        try:
            req.context.conn = await self._pool.getconn()
        except Exception:
            req.context.conn = None
            raise

    async def process_response(self, req, resp, resource, req_succeeded):
        if req.context.conn:
            await self._pool.putconn(req.context.conn)

Then, an example resource may use the connection or the pool:

class Numbers:
    async def on_get(self, req, resp):
        # This endpoint uses the connection created for the request by the Middleware
        async with req.context.conn.cursor() as cur:
            await cur.execute('SELECT value FROM numbers')
            rows = await cur.fetchall()

        resp.media = [row[0] for row in rows]

    async def on_get_with_pool(self, req, resp):
        # This endpoint uses the pool to acquire a connection
        async with req.context.pool.connection() as conn:
            cur = await conn.execute('SELECT value FROM numbers')
            rows = await cur.fetchall()
            await cur.close()

        resp.media = [row[0] for row in rows]

The application can then be used as

from falcon.asgi import App

app = App(middleware=[AsyncPoolMiddleware()])
num = Numbers()
app.add_route('/conn', num)
app.add_route('/pool', num, suffix='with_pool')

How do I test my Falcon app? Can I use pytest?

Falcon’s testing framework supports both unittest and pytest. In fact, the tutorial in the docs provides an excellent introduction to testing Falcon apps with pytest.

(See also: Testing)

How can I set cookies when simulating requests?

The easiest way is to simply pass the cookies parameter into simulate_request. Here is an example:

import falcon
import falcon.testing
import pytest

class TastyCookies:

    def on_get(self, req, resp):
        resp.media = {'cookies': req.cookies}


@pytest.fixture
def client():
    app = falcon.App()
    app.add_route('/cookies', TastyCookies())

    return falcon.testing.TestClient(app)


def test_cookies(client):
    resp = client.simulate_get('/cookies', cookies={'cookie': 'cookie value'})

    assert resp.json == {'cookies': {'cookie': 'cookie value'}}

Alternatively, you can set the Cookie header directly as demonstrated in this version of test_cookies()

def test_cookies(client):
    resp = client.simulate_get('/cookies', headers={'Cookie': 'xxx=yyy'})

    assert resp.json == {'cookies': {'xxx': 'yyy'}}

To include multiple values, simply use "; " to separate each name-value pair. For example, if you were to pass {'Cookie': 'xxx=yyy; hello=world'}, you would get {'cookies': {'xxx': 'yyy', 'hello': 'world'}}.