Cheatsheet

A quick, copy-paste reference for the most common Robyn tasks. Every snippet targets the current release. For the full story on any topic, follow the links in the navigation sidebar.

Install and run a minimal app

app.py

# pip install robyn
from robyn import Robyn

app = Robyn(__file__)

@app.get("/")
def index():
    return "Hello, world"

app.start(host="0.0.0.0", port=8080)  # host defaults to 127.0.0.1

Routes for every HTTP method

@app.get("/items")
def list_items(): ...

@app.post("/items")
def create_item(): ...

@app.put("/items/:id")
def replace_item(): ...

@app.patch("/items/:id")
def update_item(): ...

@app.delete("/items/:id")
def delete_item(): ...

# also available: @app.head, @app.options, @app.connect, @app.trace

Path parameters (single, optional, catch-all)

@app.get("/users/:id")
def get_user(request):
    return {"id": request.path_params["id"]}

@app.get("/posts/:id/:slug?")            # optional trailing segment
def get_post(request):
    return request.path_params.get("slug", "")

@app.get("/files/*path")                 # catch-all: matches the rest of the path
def read_file(request):
    return {"path": request.path_params["path"]}   # e.g. "img/2024/logo.png"

Query parameters

@app.get("/search")
def search(request):
    q = request.query_params.get("q", "")          # last value, with a default
    tags = request.query_params.get_all("tags")    # every value -> list[str]
    return {"q": q, "tags": tags}

Read request data

@app.post("/inspect")
def inspect(request):
    data = request.json()                  # parsed JSON body -> dict / list
    raw = request.body                     # raw body (str | bytes)
    form = request.form_data               # dict[str, str]
    files = request.files                  # dict[str, bytes]
    content_type = request.headers.get("Content-Type")
    return {
        "method": request.method,
        "path": request.url.path,
        "ip": request.ip_addr,
    }

Return responses

from robyn import Response, status_codes

@app.get("/text")
def text():
    return "plain text"

@app.get("/json")
def json_body():
    return {"key": "value"}            # dict / list is serialized to JSON automatically

@app.get("/custom")
def custom():
    return Response(
        status_code=status_codes.HTTP_201_CREATED,
        headers={"Content-Type": "text/plain"},
        body="created",               # `description=` is also accepted (legacy alias)
    )

Redirect

from robyn import Response

@app.get("/old")
def old():
    return Response(status_code=307, headers={"Location": "/new"}, body="")
from robyn import Response, Headers

@app.get("/login")
def login():
    response = Response(status_code=200, headers=Headers({}), body="ok")
    response.set_cookie(
        key="session", value="abc123", max_age=3600,
        path="/", http_only=True, secure=True, same_site="Strict",
    )
    return response

Middleware (before / after request)

@app.before_request()                  # global; pass a path for a per-route hook
def add_trace(request):
    request.headers.set("x-trace", "1")
    return request

@app.after_request("/")                # per-route; may take (request, response)
def stamp(request, response):
    response.headers.set("x-served", "robyn")
    return response

Authentication

from robyn import Request
from robyn.authentication import AuthenticationHandler, BearerGetter, Identity

class Auth(AuthenticationHandler):
    def authenticate(self, request: Request) -> Identity | None:
        token = self.token_getter.get_token(request)   # reads "Bearer <token>"
        if token == "valid":
            return Identity(claims={"user": "bruce"})
        return None

app.configure_authentication(Auth(token_getter=BearerGetter()))

@app.get("/me", auth_required=True)
def me(request):
    return request.identity.claims     # populated once authenticated

SubRouters

from robyn import SubRouter

api = SubRouter(prefix="/api/v1")

@api.get("/users")
def list_users():
    return {"users": []}

app.include_router(api)                # routes mounted at /api/v1/users

WebSockets

@app.websocket("/ws")
async def ws(websocket):
    while True:
        message = await websocket.receive_text()
        await websocket.send_text(f"echo: {message}")   # also: send_json(obj)
        await websocket.broadcast("to everyone")         # every client on /ws

@ws.on_connect
def on_connect(websocket):
    return "Welcome!"

@ws.on_close
def on_close(websocket):
    return "Goodbye"

Streaming and Server-Sent Events

from robyn import StreamingResponse, SSEResponse, SSEMessage

@app.get("/sse")
def sse(request):
    def events():
        for i in range(3):
            yield SSEMessage(f"tick {i}", event="tick", id=str(i))
    return SSEResponse(events())       # text/event-stream

@app.get("/stream")
def stream(request):
    def chunks():
        yield b"chunk-1"
        yield b"chunk-2"
    return StreamingResponse(chunks(), media_type="application/octet-stream")

Static files

from robyn import serve_file, serve_html

app.serve_directory(
    route="/static",
    directory_path="./assets",
    index_file="index.html",
    show_files_listing=False,
)

@app.get("/download")
def download():
    return serve_file("./report.pdf", file_name="report.pdf")   # as an attachment

@app.get("/page")
def page():
    return serve_html("./templates/index.html")

Templating (Jinja2)

from robyn.templating import JinjaTemplate

template = JinjaTemplate("./templates")

@app.get("/hello")
def hello():
    return template.render_template(template_name="hello.html", name="Bruce")

Raise an HTTP error

from robyn.exceptions import HTTPException     # note: robyn.exceptions, not robyn

@app.get("/users/:id")
def get_user(request):
    if not request.path_params["id"].isdigit():
        raise HTTPException(400, "id must be numeric")   # (status_code, detail)
    return {"id": request.path_params["id"]}

Const requests (computed once, cached in Rust)

@app.get("/health", const=True)
def health():
    return {"status": "healthy"}

Dependency injection

app.inject_global(DB="global-db")      # available to every route
app.inject(CACHE="router-cache")       # available to this router's routes

@app.get("/data")
def data(request, global_dependencies, router_dependencies):
    return {
        "db": global_dependencies["DB"],
        "cache": router_dependencies["CACHE"],
    }

CORS

from robyn import Robyn, ALLOW_CORS

app = Robyn(__file__)
ALLOW_CORS(app, origins=["http://localhost:3000"])   # or origins="*"

OpenAPI / Swagger

@app.get("/users", openapi_name="List Users", openapi_tags=["Users"])
def list_users():
    return {"users": []}

# Swagger UI is served at /docs and the spec at /openapi.json

Lifecycle events

@app.startup_handler
async def on_startup():
    print("starting up")

@app.shutdown_handler
def on_shutdown():
    print("shutting down")

Scaling and CLI flags

python app.py --processes 4 --workers 2    # spread across cores / threads
python app.py --fast                       # auto-tune processes, workers and log level
python app.py --dev                        # auto-reload on change (single process)
python app.py --log-level WARNING          # DEBUG / INFO / WARNING / ERROR