NEW: Fenrir v4.1.2 — 56 Bug Fixes, Performance Optimizations, 1331 Tests, 5-Framework Benchmark! Read the changelog
basic-concepts.md
docs basic-concepts.md

Basic Concepts

Fenrir Application

1
2
3
4
5
6
7
8
9
from fenrir import Fenrir

app = Fenrir(
    title="My Application",
    version="4.1.2",
    docs_url="/docs",
    redoc_url="/redoc",
    openapi_url="/openapi.json"
)

Constructor Parameters

Parameter Type Default Description
import_name str None Import name for root path detection (auto-detected from caller)
title str "Fenrir API" Application title (shown in OpenAPI docs)
version str "4.1.2" Application version
docs_url str "/docs" Swagger UI endpoint path (None to disable)
redoc_url str "/redoc" ReDoc endpoint path (None to disable)
openapi_url str "/openapi.json" OpenAPI schema endpoint path (None to disable)
template_folder str "templates" Template directory name
renderer BaseTemplateRenderer None Custom template renderer (default: Jinja2)
root_path str None ASGI root path for reverse proxies (auto-detected)
instance_path str None Path to instance folder (default: root_path/instance)
instance_relative_config bool False Whether config is loaded relative to instance_path
strict_content_type bool False Require application/json content-type for JSON bodies
route_class Type[Route] None Custom route class for the router
dev_mode bool None Enable debug pages with stack traces (auto-enabled from FENRIR_DEV_MODE=1 env var)
1
2
3
4
5
6
app = Fenrir(
    title="My API",
    version="1.0.0",
    strict_content_type=True,    # Require application/json
    instance_path="/var/myapp",  # Custom instance folder
)

App Attributes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
app.title                     # str — app title
app.version                   # str — app version
app.config                    # Config — configuration object
app.json                      # DefaultJSONProvider — JSON serializer
app.renderer                  # Jinja2Renderer — template renderer
app.session_interface         # SessionInterface — session backend
app.router                    # Router — route registration system
app.root_path                 # str — resolved root path
app.instance_path             # str — instance directory path
app.template_folder           # str — templates directory name
app.docs_url                  # str — Swagger UI endpoint
app.redoc_url                 # str — ReDoc endpoint
app.openapi_url               # str — OpenAPI schema endpoint
app.strict_content_type       # bool — require JSON content-type
app.blueprints                # List[Blueprint] — registered blueprints
app.exception_handlers        # Dict — registered error handlers
app.listeners                 # Dict — lifecycle event listeners
app.teardown_request_funcs    # Dict — request teardown functions
app.teardown_appcontext_funcs # List — app-context teardown functions
app.dependency_overrides      # Dict — DI overrides for testing
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Debug mode (enables debug pages with stack traces)
app = Fenrir(dev_mode=True)
# Or via environment variable: FENRIR_DEV_MODE=1

# Configuration
app.config["DEBUG"] = True
app.config["SECRET_KEY"] = "my-secret"
app.config["DATABASE_URL"] = "postgresql://localhost/mydb"

# Custom JSON serialization (uses orjson when available)
from fenrir.json import json_dumps, json_loads
data = json_dumps({"key": "value"})  # 7x faster with orjson

App Methods

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Routing
app.route(path, methods=None, **kwargs)          # Decorator for routes
app.get(path, **kwargs)                          # GET route
app.post(path, **kwargs)                         # POST route
app.put(path, **kwargs)                          # PUT route
app.delete(path, **kwargs)                       # DELETE route
app.patch(path, **kwargs)                        # PATCH route
app.add_route(path, handler, methods=None)       # Programmatic route
app.add_websocket_route(path, handler)           # WebSocket route
app.include_router(router, prefix="")            # Include APIRouter
app.websocket(path, timeout=None)                # WebSocket decorator

# Middleware
app.add_middleware(middleware_class, **options)   # Add ASGI middleware
app.before_request(f)                            # Request middleware
app.after_request(f)                             # Response middleware
app.middleware(middleware_type)                   # Middleware decorator

# Blueprints
app.register_blueprint(blueprint)                # Register a Blueprint

# Error handlers
app.exception(*exceptions)                       # Error handler decorator
app.register_error_handler(exc_class, handler)   # Programmatic error handler

# Lifecycle
app.listener(event_name)                         # Lifecycle listener
app.teardown_request(f)                          # Request teardown
app.teardown_appcontext(f)                       # App-context teardown
app.add_task(coro)                               # Schedule async task

# WSGI
app.mount_wsgi(path, wsgi_app)                   # Mount WSGI app

# OpenAPI
app.openapi()                                    # Generate OpenAPI schema

# Testing
app.test_client()                                # Create test client
app.test_request_context(*args, **kwargs)        # Create test request context
app.app_context()                                # Create app context

HTTP Methods

Fenrir supports all standard HTTP methods:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@app.get("/path")
async def get_handler():
    pass

@app.post("/path")
async def post_handler():
    pass

@app.put("/path")
async def put_handler():
    pass

@app.delete("/path")
async def delete_handler():
    pass

@app.patch("/path")
async def patch_handler():
    pass

Automatic methods:

  • HEAD is automatically added for any route that supports GET (RFC 7231)
  • OPTIONS is automatically added for all routes (CORS support)

Multi-Method Routes

1
2
3
@app.route("/resource", methods=["GET", "POST"])
async def resource():
    return {"message": "Resource"}

Adding Middleware

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from fenrir import CORSMiddleware, GZipMiddleware

# ASGI middleware
app.add_middleware(CORSMiddleware, allow_origins=["*"])
app.add_middleware(GZipMiddleware, minimum_size=500)

# Request/Response middleware
@app.before_request
async def log_request():
    print(f"Request: {request.method} {request.path}")

@app.after_request
async def add_headers(response):
    response.headers["X-Custom"] = "value"
    return response

Adding Routes Programmatically

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Function-based route
app.add_route("/users", list_users)

# Class-based resource
app.add_route("/items/<item_id:int>", ItemResource())

# WebSocket route
app.add_websocket_route("/ws", websocket_handler)

# APIRouter
from fenrir import APIRouter

router = APIRouter()

@router.get("/users")
async def list_users():
    return [{"id": 1}]

app.include_router(router, prefix="/api/v1")

Blueprints

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from fenrir import Blueprint

api_bp = Blueprint("api", url_prefix="/api")

@api_bp.get("/items")
async def list_items():
    return [{"id": 1}]

@api_bp.before_request
async def check_auth():
    if not request.headers.get("Authorization"):
        raise HTTPUnauthorized()

app.register_blueprint(api_bp)

Lifecycle Listeners

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@app.listener("before_server_start")
async def on_startup(app_instance):
    print("Server starting")
    # Initialize database pool, etc.

@app.listener("after_server_start")
async def on_after_startup(app_instance):
    print("Server started")

@app.listener("before_server_stop")
async def on_before_stop(app_instance):
    print("Server stopping")

@app.listener("after_server_stop")
async def on_shutdown(app_instance):
    print("Server stopped")

Exception Handlers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from fenrir import HTTPNotFound, HTTPException

@app.exception(404)
async def not_found(req, exc):
    return {"error": "Not found"}, 404

@app.exception(HTTPException)
async def handle_http(req, exc):
    return {"error": exc.detail}, exc.status_code

@app.exception(500, 502, 503)
async def server_errors(req, exc):
    return {"error": "Server error"}, 500

Dependency Injection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from fenrir import Depends

async def get_db():
    db = await create_connection()
    try:
        yield db
    finally:
        await db.close()

async def get_current_user(token: str = Header(default=None)):
    if not token:
        raise HTTPUnauthorized()
    return {"user_id": 1}

@app.get("/users")
async def list_users(
    db = Depends(get_db),
    user = Depends(get_current_user)
):
    return await db.fetch("SELECT * FROM users")

# Override for testing
app.dependency_overrides[get_db] = lambda: mock_db

Background Tasks

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from fenrir import BackgroundTasks

async def send_email(email: str):
    await asyncio.sleep(2)
    print(f"Email sent to {email}")

@app.post("/send-email")
async def send(background_tasks: BackgroundTasks):
    background_tasks.add_task(send_email, "user@example.com")
    return {"message": "Email queued"}

Running the Application

1
2
if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000)

app.run() parameters:

Parameter Type Default Description
host str "127.0.0.1" Bind host
port int 8000 Bind port
workers int 1 Number of worker processes
app_path str None App module path (auto-detected)

CLI commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Run development server
fenrir run app:app --host 0.0.0.0 --port 8000 --dev

# Run with hot reload
fenrir run app:app --reload

# List all routes
fenrir routes app:app

# Start interactive shell
fenrir shell app:app

# Run benchmarks
fenrir bench app:app -i 2000 -t 5

# Create new project
fenrir new myapp

# Show system info
fenrir info app:app
Edit on GitHub Last Updated: Oct 20, 2018
© 2026 Fenrir Project.
main*
v4.1.2
Ln 1, Col 1
UTF-8
Prettier
Light Mode
Markdown