Falcon-high-performance web frame

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Falcon

Falcon is a minimalist, high‑performance web framework for Python, specifically designed for building scalable REST APIs. It is optimized for low latency and high throughput, making it an ideal choice for microservices, embedded systems, IoT applications, as well as enterprise solutions that demand stability and speed.

What is Falcon and Why Use It

Falcon positions itself as a “pure” web framework that doesn’t impose a particular architecture or solution approach on developers. Its primary goal is to deliver maximum performance while consuming minimal resources. The framework follows the principle “do one thing, and do it well,” focusing exclusively on HTTP request handling and REST API construction.

Key Features and Benefits of Falcon

High Performance

Falcon delivers performance comparable to Go and Node.js thanks to an optimized architecture and minimal overhead.

Low Resource Consumption

The framework uses significantly less memory and CPU time than other Python frameworks.

Clean REST Architecture

Falcon was built from the ground up for REST APIs, which is reflected in its design and request‑handling model.

Full Control Over Requests and Responses

Developers get direct access to request and response objects, providing maximum flexibility.

Modern Technology Support

  • Middleware for request processing
  • Hooks for additional logic
  • CORS handling for cross‑domain requests
  • ASGI and WSGI compatibility (Falcon 3.0+)

Easy Integration

Integrates smoothly with any SQL or NoSQL database without enforcing a specific ORM.

Falcon vs. Other Frameworks

Feature Falcon Flask FastAPI Django REST
Performance Very high Medium High Low
ASGI Support Yes (3.0+) No (WSGI) Yes Yes
Typing Optional Not used Built‑in Optional
Sync/Async Support Yes Sync only Full async Yes
REST‑Ready Yes (best) Yes Yes Yes
Framework Size Minimal Medium Medium Large
Learning Curve Low Low Medium High

Installation and Project Setup

Basic Installation

pip install falcon

ASGI Application

pip install falcon[asgi]

Installation with Extra Dependencies

pip install falcon[asgi,cors,yaml]

Creating Your First Falcon Application

import falcon

class HelloResource:
    def on_get(self, req, resp):
        resp.media = {'message': 'Hello, Falcon!'}

# Create the app
app = falcon.App()

# Add a route
app.add_route('/hello', HelloResource())

# Run with the built‑in server (development only)
if __name__ == '__main__':
    from wsgiref import simple_server
    httpd = simple_server.make_server('127.0.0.1', 8000, app)
    httpd.serve_forever()

HTTP Method Handling

Falcon uses a naming convention on resource classes to map HTTP methods:

class APIResource:
    def on_get(self, req, resp):
        """Handle GET requests"""
        resp.media = {"method": "GET", "data": "Fetching data"}
    
    def on_post(self, req, resp):
        """Handle POST requests"""
        data = req.media
        resp.media = {"method": "POST", "received": data}
        resp.status = falcon.HTTP_201
    
    def on_put(self, req, resp):
        """Handle PUT requests"""
        data = req.media
        resp.media = {"method": "PUT", "updated": data}
        resp.status = falcon.HTTP_200
    
    def on_delete(self, req, resp):
        """Handle DELETE requests"""
        resp.status = falcon.HTTP_204
    
    def on_patch(self, req, resp):
        """Handle PATCH requests"""
        data = req.media
        resp.media = {"method": "PATCH", "patched": data}

Working with Query Parameters

Query‑String Parameters

class SearchResource:
    def on_get(self, req, resp):
        # Get a parameter with a default value
        query = req.get_param('q', default='')
        limit = req.get_param_as_int('limit', default=10)
        
        # Get all parameters
        all_params = req.params
        
        resp.media = {
            'query': query,
            'limit': limit,
            'all_params': all_params
        }

Path Variables

class UserResource:
    def on_get(self, req, resp, user_id):
        resp.media = {'user_id': user_id}
    
    def on_put(self, req, resp, user_id):
        data = req.media
        resp.media = {'user_id': user_id, 'updated_data': data}

# Register the route with a variable
app.add_route('/users/{user_id}', UserResource())

Multiple Path Variables

class PostResource:
    def on_get(self, req, resp, user_id, post_id):
        resp.media = {
            'user_id': user_id,
            'post_id': post_id
        }

app.add_route('/users/{user_id}/posts/{post_id}', PostResource())

JSON Handling and Data Processing

Automatic JSON Support

class DataResource:
    def on_post(self, req, resp):
        # Automatic JSON decoding
        data = req.media
        
        # Simple validation
        if not data or 'name' not in data:
            raise falcon.HTTPBadRequest(
                title='Invalid data',
                description='The "name" field is required'
            )
        
        # Process the payload
        processed_data = {
            'id': 123,
            'name': data['name'],
            'created_at': '2024-01-01T00:00:00Z'
        }
        
        # Automatic JSON serialization
        resp.media = processed_data
        resp.status = falcon.HTTP_201

File Uploads

class FileUploadResource:
    def on_post(self, req, resp):
        # Retrieve the uploaded file
        upload = req.get_param('file')
        
        if upload is None:
            raise falcon.HTTPBadRequest(
                title='File not found',
                description='A file must be uploaded'
            )
        
        # Save the file
        filename = upload.filename
        with open(f'uploads/{filename}', 'wb') as f:
            while True:
                chunk = upload.file.read(4096)
                if not chunk:
                    break
                f.write(chunk)
        
        resp.media = {'filename': filename, 'status': 'uploaded'}

Error and Exception Handling

Built‑in Exceptions

class ValidationResource:
    def on_post(self, req, resp):
        data = req.media
        
        if not data:
            raise falcon.HTTPBadRequest(
                title='Missing data',
                description='Request body cannot be empty'
            )
        
        if 'email' not in data:
            raise falcon.HTTPUnprocessableEntity(
                title='Invalid format',
                description='The "email" field is required'
            )
        
        resp.media = {'status': 'validated'}

Custom Exceptions

class CustomError(falcon.HTTPError):
    def __init__(self, message):
        super().__init__(
            status=falcon.HTTP_400,
            title='Custom error',
            description=message
        )

class BusinessLogicResource:
    def on_post(self, req, resp):
        data = req.media
        
        if data.get('age', 0) < 18:
            raise CustomError('Age must be at least 18')
        
        resp.media = {'status': 'ok'}

Global Error Handlers

def handle_database_error(req, resp, ex, params):
    resp.status = falcon.HTTP_500
    resp.media = {
        'error': 'Database error',
        'message': 'Please try again later'
    }

def handle_validation_error(req, resp, ex, params):
    resp.status = falcon.HTTP_422
    resp.media = {
        'error': 'Validation error',
        'details': str(ex)
    }

# Register handlers
app.add_error_handler(DatabaseError, handle_database_error)
app.add_error_handler(ValidationError, handle_validation_error)

Middleware in Falcon

Middleware lets you run code before and after request processing:

Basic Middleware

class LoggingMiddleware:
    def process_request(self, req, resp):
        print(f"Incoming request: {req.method} {req.path}")
    
    def process_response(self, req, resp, resource, req_succeeded):
        print(f"Response: {resp.status}")

app = falcon.App(middleware=[LoggingMiddleware()])

Authentication Middleware

class AuthMiddleware:
    def process_request(self, req, resp):
        # Skip public endpoints
        if req.path in ['/health', '/login']:
            return
        
        token = req.get_header('Authorization')
        if not token:
            raise falcon.HTTPUnauthorized(
                title='Authentication required',
                description='A token must be provided'
            )
        
        # Token validation
        if not self.validate_token(token):
            raise falcon.HTTPUnauthorized(
                title='Invalid token',
                description='The provided token is not valid'
            )
        
        # Store user info for downstream handlers
        req.context.user = self.get_user_from_token(token)
    
    def validate_token(self, token):
        # Simple validation logic
        return token == 'valid_token'
    
    def get_user_from_token(self, token):
        return {'id': 1, 'name': 'user'}

CORS Middleware

class CORSMiddleware:
    def process_request(self, req, resp):
        resp.set_header('Access-Control-Allow-Origin', '*')
        resp.set_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
        resp.set_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
    
    def process_response(self, req, resp, resource, req_succeeded):
        if req.method == 'OPTIONS':
            resp.status = falcon.HTTP_200

Hooks in Falcon

Hooks allow you to run additional logic before or after a resource is processed:

Before and After Hooks

def validate_json(req, resp, resource, params):
    if req.content_length and req.content_type != 'application/json':
        raise falcon.HTTPUnsupportedMediaType(
            description='Only JSON is supported'
        )

def add_response_headers(req, resp, resource):
    resp.set_header('X-API-Version', '1.0')
    resp.set_header('X-Response-Time', '100ms')

class APIResource:
    @falcon.before(validate_json)
    @falcon.after(add_response_headers)
    def on_post(self, req, resp):
        data = req.media
        resp.media = {'received': data}

Database Integration

SQLAlchemy Example

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Database setup
engine = create_engine('sqlite:///example.db')
Base = declarative_base()
Session = sessionmaker(bind=engine)

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    email = Column(String(100))

Base.metadata.create_all(engine)

class DatabaseMiddleware:
    def process_request(self, req, resp):
        req.context.db = Session()
    
    def process_response(self, req, resp, resource, req_succeeded):
        if hasattr(req.context, 'db'):
            req.context.db.close()

class UserResource:
    def on_get(self, req, resp):
        users = req.context.db.query(User).all()
        resp.media = [{'id': u.id, 'name': u.name, 'email': u.email} for u in users]
    
    def on_post(self, req, resp):
        data = req.media
        user = User(name=data['name'], email=data['email'])
        req.context.db.add(user)
        req.context.db.commit()
        resp.media = {'id': user.id, 'name': user.name, 'email': user.email}
        resp.status = falcon.HTTP_201

Testing Falcon APIs

Test Environment Setup

import pytest
import falcon
from falcon import testing

class TestResource:
    def on_get(self, req, resp):
        resp.media = {'message': 'test'}

@pytest.fixture
def app():
    api = falcon.App()
    api.add_route('/test', TestResource())
    return api

@pytest.fixture
def client(app):
    return testing.TestClient(app)

Sample Tests

def test_get_request(client):
    response = client.simulate_get('/test')
    assert response.status_code == 200
    assert response.json == {'message': 'test'}

def test_post_request(client):
    data = {'name': 'Test User'}
    response = client.simulate_post('/test', json=data)
    assert response.status_code == 201

def test_error_handling(client):
    response = client.simulate_get('/nonexistent')
    assert response.status_code == 404

Async Support in Falcon 3.0+

Creating an ASGI Application

import falcon.asgi
import asyncio

class AsyncResource:
    async def on_get(self, req, resp):
        # Simulate async work
        await asyncio.sleep(0.1)
        resp.media = {'message': 'async response'}
    
    async def on_post(self, req, resp):
        data = await req.get_media()
        # Async processing
        result = await self.process_data(data)
        resp.media = {'result': result}
    
    async def process_data(self, data):
        await asyncio.sleep(0.1)
        return {'processed': True}

# Build the ASGI app
app = falcon.asgi.App()
app.add_route('/async', AsyncResource())

Async Middleware

class AsyncMiddleware:
    async def process_request(self, req, resp):
        # Async validation step
        await self.validate_async(req)
    
    async def validate_async(self, req):
        await asyncio.sleep(0.01)
        return True

Deploying Falcon Applications

Gunicorn (WSGI) Deployment

# Basic start
gunicorn app:app

# Production configuration
gunicorn app:app \
    --workers 4 \
    --bind 0.0.0.0:8000 \
    --worker-class sync \
    --timeout 30 \
    --keepalive 2

Uvicorn (ASGI) Deployment

# Basic start
uvicorn app:app

# Production configuration
uvicorn app:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --loop uvloop \
    --http httptools

Docker Container

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]

Performance and Optimization

Optimization Tips

  1. Use connection pooling for databases
  2. Cache results wherever possible
  3. Minimize middleware – keep only what you need
  4. Leverage ASGI for async workloads
  5. Configure the right number of workers

Optimized Application Example

import falcon
from falcon_caching import Cache

# Set up caching
cache = Cache(config={'CACHE_TYPE': 'redis'})

class OptimizedResource:
    @cache.cached(timeout=300)
    def on_get(self, req, resp):
        # Expensive operation, cached for 5 minutes
        resp.media = self.expensive_operation()
    
    def expensive_operation(self):
        # Simulated heavy computation
        return {'data': 'expensive result'}

Falcon Method and Function Reference

Core Components

Component Description Usage Example
falcon.App() Creates an application instance app = falcon.App()
falcon.asgi.App() Creates an ASGI application app = falcon.asgi.App()
app.add_route() Registers a route app.add_route('/api', Resource())
app.add_error_handler() Registers a custom error handler app.add_error_handler(Exception, handler)

HTTP Methods in Resources

Method Description HTTP Verb
on_get() Handles GET requests GET
on_post() Handles POST requests POST
on_put() Handles PUT requests PUT
on_delete() Handles DELETE requests DELETE
on_patch() Handles PATCH requests PATCH
on_head() Handles HEAD requests HEAD
on_options() Handles OPTIONS requests OPTIONS

Request Object (req)

Attribute / Method Description Example
req.method HTTP method of the request 'GET', 'POST'
req.path Request path '/api/users'
req.url Full URL 'http://example.com/api'
req.query_string Raw query string 'page=1&limit=10'
req.headers Request headers req.headers['Authorization']
req.get_header() Retrieve a single header req.get_header('Content-Type')
req.get_param() Get a query parameter req.get_param('page')
req.get_param_as_int() Parameter as an integer req.get_param_as_int('limit')
req.get_param_as_bool() Parameter as a boolean req.get_param_as_bool('active')
req.params All parameters as a dict dict containing every query param
req.media Parsed JSON body Automatically decoded JSON payload
req.bounded_stream Raw request body stream Used for reading non‑JSON bodies
req.content_length Length of the request body Size in bytes
req.content_type MIME type of the request 'application/json'
req.context Per‑request context storage Pass data between middleware and resources

Response Object (resp)

Attribute / Method Description Example
resp.status HTTP status code falcon.HTTP_200
resp.text Plain‑text body resp.text = 'Hello'
resp.media JSON body resp.media = {'key': 'value'}
resp.data Binary payload resp.data = b'binary data'
resp.content_type Response MIME type 'application/json'
resp.set_header() Set a response header resp.set_header('X-Custom', 'value')
resp.set_cookie() Set a cookie resp.set_cookie('session', 'value')
resp.location Location header for redirects resp.location = '/new-resource'
resp.cache_control Cache‑Control header value resp.cache_control = 'no-cache'

HTTP Status Constants

Constant Code Description
falcon.HTTP_200 200 OK
falcon.HTTP_201 201 Created
falcon.HTTP_204 204 No Content
falcon.HTTP_400 400 Bad Request
falcon.HTTP_401 401 Unauthorized
falcon.HTTP_403 403 Forbidden
falcon.HTTP_404 404 Not Found
falcon.HTTP_405 405 Method Not Allowed
falcon.HTTP_409 409 Conflict
falcon.HTTP_422 422 Unprocessable Entity
falcon.HTTP_500 500 Internal Server Error

Exception Types

Exception Description Typical Use
falcon.HTTPBadRequest 400 – Bad request Validation errors
falcon.HTTPUnauthorized 401 – Unauthorized Missing authentication
falcon.HTTPForbidden 403 – Forbidden Insufficient permissions
falcon.HTTPNotFound 404 – Not found Resource does not exist
falcon.HTTPMethodNotAllowed 405 – Method not allowed Unsupported HTTP verb
falcon.HTTPConflict 409 – Conflict Data conflict situations
falcon.HTTPInternalServerError 500 – Internal server error Server‑side failures

Hooks

Hook Description Typical Use
@falcon.before() Runs before resource processing Validation, authentication
@falcon.after() Runs after resource processing Logging, response modification

Middleware Methods

Method Description Parameters
process_request() Executed before routing req, resp
process_resource() Executed after routing but before the resource method req, resp, resource, params
process_response() Executed after the resource method req, resp, resource, req_succeeded

Practical Use Cases

High‑Throughput REST APIs

Falcon excels at building APIs that must handle massive traffic where request latency is critical.

Microservices

Its tiny footprint and rapid startup make Falcon a perfect fit for microservice architectures.

IoT and Embedded Systems

Low resource usage makes Falcon suitable for IoT devices and other constrained environments.

API Gateways

Falcon can serve as a high‑performance API gateway, routing requests between backend services.

Real‑Time Services

ASGI support in Falcon 3.0+ enables efficient real‑time communication services.

Frequently Asked Questions

What is Falcon and how does it differ from other frameworks?

Falcon is a minimalist Python web framework optimized exclusively for building REST APIs. It stands out with its high performance, low overhead, and clean architecture.

Does Falcon support async operations?

Yes. Starting with version 3.0, Falcon offers async capabilities via ASGI, allowing you to write asynchronous resources and middleware.

How does Falcon handle JSON?

Falcon automatically parses incoming JSON into req.media and serializes resp.media back to JSON. No manual parsing or dumping is required.

Is Falcon suitable for large projects?

Absolutely. Falcon scales well in large, microservice‑based systems and is used by major companies for high‑load API platforms.

Can Falcon work with databases?

Yes. Falcon imposes no ORM choice and integrates easily with any Python database library, such as SQLAlchemy, Tortoise ORM, asyncpg, PyMongo, and others.

How do I ensure security in Falcon?

Security is achieved through middleware (authentication, authorization), input validation, proper error handling, and always serving over HTTPS.

What performance can I expect from Falcon?

Falcon delivers some of the highest performance among Python web frameworks, comparable to Go and Node.js implementations.

Can I use Falcon to build full‑stack websites?

Falcon is purpose‑built for REST APIs and is not intended for serving traditional HTML pages. For full‑stack web apps, consider Flask or Django.

News