Request Object

The request object is a dataclass that contains all the information about the request. It is available in the route handler as the first argument.

The request object is created in Rust side but is exposed to Python as a dataclass.

  • Attributes:

  • query_params (QueryParams): The query parameters of the request. e.g. /user?id=123 -> {"id": [ "123" ]}

  • headers (dict[str, str]): The headers of the request. e.g. {"Content-Type": "application/json"}

  • params (dict[str, str]): The parameters of the request. e.g. /user/:id -> {"id": "123"}

  • body (Union[str, bytes]): The raw body of the request. For JSON payloads, use the json() method to parse the body into a dict with proper type preservation.

  • method (str): The method of the request. e.g. GET, POST, PUT, DELETE

  • ip_addr (Optional[str]): The IP Address of the client

  • identity (Optional[Identity]): The identity of the client

Request

GET
/hello_world
@dataclass
class Request:
  """
  query_params: QueryParams
  headers: Headers
  path_params: dict[str, str]
  body: Union[str, bytes]
  method: str
  url: Url
  form_data: dict[str, str]
  files: dict[str, bytes]
  ip_addr: Optional[str]
  identity: Optional[Identity]
  """

Parsing JSON Body

The request.json() method parses the request body as JSON and returns a Python dict with full type preservation:

  • JSON null becomes Python None
  • JSON numbers become Python int or float
  • JSON booleans become Python bool
  • JSON strings become Python str
  • JSON arrays become Python list
  • JSON objects become Python dict

Nested structures are handled recursively up to a maximum depth of 128 levels.

Parsing JSON

POST
/example
@app.post("/example")
async def handler(request: Request):
    data = request.json()  # Returns a dict with preserved types
    # e.g. {"count": 42, "active": true, "tags": ["a", "b"]}
    # ->   {"count": 42, "active": True, "tags": ["a", "b"]}
    return {"received": data}

If the body is not valid JSON or is not a JSON object, a ValueError will be raised.

Extra Path Parameters

Robyn supports capturing extra path parameters using the *extra syntax in route definitions. This allows you to capture any additional segments in the URL path that come after the defined route.

For example, if you define a route like this:

@app.get("/sync/extra/*extra")
def sync_param_extra(request: Request):
    extra = request.path_params["extra"]
    return extra

Any additional path segments after /sync/extra/ will be captured in the extra parameter. For instance:

  • A request to /sync/extra/foo/bar would result in extra = "foo/bar"

  • A request to /sync/extra/123/456/789 would result in extra = "123/456/789"

You can access the extra path parameters through request.path_params["extra"] in your route handler.

This feature is particularly useful when you need to handle dynamic, nested routes or when you want to capture an unknown number of path segments.

Easy Access Parameters

Instead of manually extracting and converting query parameters and path parameters from the request object, you can declare them directly in your function signature with type annotations. Robyn will automatically resolve and coerce them for you.

Any handler parameter that doesn't match a known request component (Request, QueryParams, Headers, etc.) is treated as an individual path or query parameter.

Basic usage — path params and query params with type coercion and defaults.

Easy Access Params

GET
/items/:id?q=hello&page=5
@app.get("/items/:id")
async def get_item(id: int, q: str, page: int = 1):
    # id is coerced from the path param string to int
    # q is taken from ?q=...
    # page defaults to 1 if not provided
    return {"id": id, "q": q, "page": page}

Optional, List, Bool, and Float params — Robyn handles common Python types automatically.

  • Optional[T] — resolves to None when not provided
  • List[T] — collects repeated query params (e.g. ?tag=a&tag=b)
  • bool — accepts true/false, 1/0, yes/no, on/off
  • float — standard float coercion

Advanced Types

GET
/search
@app.get("/search")
def search(name: str, age: Optional[int] = None):
    return {"name": name, "age": age}
# GET /search?name=bob        -> {"name": "bob", "age": null}
# GET /search?name=bob&age=30 -> {"name": "bob", "age": 30}

Error handling — if a required parameter is missing or a value cannot be coerced to the declared type, Robyn returns a 400 Bad Request response automatically.

Validation Errors

GET
/items/:id
@app.get("/items/:id")
def get_item(id: int, q: str):
    return {"id": id, "q": q}

# GET /items/42         -> 400 (missing required 'q')
# GET /items/abc?q=test -> 400 (cannot coerce 'abc' to int)

What's next?

Now, Batman wanted to understand the configuration of the Robyn server. He was then introduced to the concept of Robyn env files.