Parameters¶
Veloce binds handler arguments to parts of the request by name and type.
When the defaults are not enough, the parameter marker classes —
Query, Path,
Body, Form,
File, Header,
and Cookie — let you set an alias, a
default, validation constraints, and OpenAPI metadata.
from veloce import Query, Request, Veloce
app = Veloce()
@app.get("/search")
async def search(request: Request, q: str = Query(default=""), page: int = Query(default=1, ge=1)):
return {"q": q, "page": page}
/search?q=veloce&page=2 yields q="veloce", page=2. A request with
page=0 is rejected with a 422 because of the ge=1 constraint.
How binding works without markers¶
You do not need a marker for the common case. A handler parameter is bound by
these rules, in order: a name that matches a path segment is a path
parameter; a Request annotation receives the request; a Pydantic model is
read from the body; anything else is read from the query string. A default
value makes the parameter optional. See Routing
for the marker-free form.
Reach for a marker when you need to rename a parameter, validate it, mark it optional with constraints, or document it in the OpenAPI schema.
Shared constructor¶
All seven classes share the same constructor. The first argument, default,
defaults to the Ellipsis sentinel (...), which means the parameter is
required. Supply any other value to make it optional.
The most useful keyword arguments are:
| Argument | Meaning |
|---|---|
default |
Value used when the parameter is absent; omit it to make the parameter required. |
default_factory |
A zero-argument callable invoked on every request the parameter is absent, so each request gets its own value. Use it for mutable defaults (default_factory=list) instead of a shared default=[]. Mutually exclusive with default. |
alias |
The wire name to read from, when it differs from the Python argument name. |
title, description |
OpenAPI documentation strings. |
ge, le, gt, lt |
Numeric bounds for int / float / Decimal values. |
min_length, max_length |
Length bounds for str values. |
multiple_of |
Require a numeric value to be a multiple of this (must be positive). |
pattern |
A regular expression the full string value must match. |
deprecated |
Mark the parameter deprecated in the schema. |
examples |
A list of example values for the schema. |
include_in_schema |
When False, the parameter is still resolved but omitted from the OpenAPI parameters list. |
Note
pattern is the current name for the regex constraint; the older regex
keyword is still accepted, and pattern wins if you pass both. Passing a
non-positive multiple_of raises ValueError at startup rather than
producing a non-conformant schema.
Mutable defaults
A static mutable default — Query(default=[]) — is constructed once and
shared by every request, so an in-place mutation by one handler leaks into
the next. Veloce warns at startup when it sees a list, dict, or set
static default and points you at default_factory, which builds a fresh
value per request:
Query¶
Query reads from the URL query string. Use
alias to bind a wire name that is not a valid Python identifier, and the
constraint arguments to validate the value.
from veloce import Query, Request, Veloce
app = Veloce()
@app.get("/items")
async def list_items(
request: Request,
q: str = Query(default="", max_length=50, description="Search text"),
page: int = Query(default=1, ge=1),
):
return {"q": q, "page": page}
A list-typed parameter collects repeated query keys. Annotate it with
list[...]:
from veloce import Request, Veloce
app = Veloce()
@app.get("/tags")
async def by_tags(request: Request, tag: list[str] = []):
return {"tags": tag} # /tags?tag=a&tag=b -> ["a", "b"]
A bool-annotated query, path, header, or cookie parameter is coerced from
the raw string.
The value is true only when it lower-cases to one of true,
1, or yes; every other string (including false, 0, no, off, and the
empty string) coerces to False.
from veloce import Request, Veloce
app = Veloce()
@app.get("/items")
async def list_items(request: Request, archived: bool = False):
return {"archived": archived} # /items?archived=yes -> True
Bool coercion accepts a fixed token set
Unlike FastAPI, which treats on/off as valid bool tokens, Veloce only
recognises true, 1, and yes (case-insensitively) as True.
Anything else is False — there is no 422 for an unrecognised value, so a
typo like archived=ture silently reads as False.
The same token set applies to Query, Path, Header, and Cookie
parameters.
Path¶
Path annotates a value taken from a path
segment. The argument name must match the {...} placeholder in the route.
Path parameters are always required, so a default is not meaningful, but the
constraints and documentation arguments still apply.
from veloce import Path, Veloce
app = Veloce()
@app.get("/items/{item_id}")
async def get_item(item_id: int = Path(ge=1, description="Item ID")):
return {"id": item_id}
Body¶
Body binds the raw request body, or a named
field within a JSON body when embed=True. For structured payloads, prefer a
Pydantic model (see Requests & responses); use
Body for a single scalar value.
from veloce import Body, Veloce
app = Veloce()
@app.post("/echo")
async def echo(message: str = Body(embed=True)):
# expects {"message": "..."} in the JSON body
return {"message": message}
The embed argument is only meaningful for Body. Without it the whole body
is treated as the value.
Form¶
Form reads a field from an
application/x-www-form-urlencoded or multipart/form-data body.
from veloce import Form, Veloce
app = Veloce()
@app.post("/login")
async def login(username: str = Form(), password: str = Form(min_length=8)):
return {"user": username}
File¶
File declares an uploaded file field in a
multipart/form-data body. To receive the upload itself, annotate the
argument with UploadFile.
from veloce import File, UploadFile, Veloce
app = Veloce()
@app.post("/upload")
async def upload(document: UploadFile = File(description="The file to store")):
contents = await document.read()
return {"filename": document.filename, "size": len(contents)}
See File uploads for the full UploadFile API.
Header¶
Header reads an HTTP request header. By
default an un-aliased header argument has its underscores rewritten to
hyphens, so x_token reads the X-Token header. Set
convert_underscores=False to disable that, or pass an explicit alias.
from veloce import Header, Request, Veloce
app = Veloce()
@app.get("/whoami")
async def whoami(request: Request, x_token: str = Header(default="")):
return {"token": x_token} # reads the X-Token header
Header names are case-insensitive per RFC 9110.
When you supply alias=, the alias is used verbatim and underscore
conversion does not apply.
Cookie¶
Cookie reads a single cookie value.
from veloce import Cookie, Request, Veloce
app = Veloce()
@app.get("/session")
async def read_session(request: Request, session_id: str = Cookie(default=None)):
return {"session_id": session_id}
Declaring markers with Annotated¶
Following PEP 593, you can attach a
marker through Annotated instead of the default value. This keeps the
argument's real default free for an ordinary value.
from typing import Annotated
from veloce import Query, Request, Veloce
app = Veloce()
@app.get("/search")
async def search(request: Request, q: Annotated[str, Query(max_length=50)] = ""):
return {"q": q}
The marker in the Annotated metadata supplies the constraints; the value
after = supplies the default. If you set the marker as the default and in
Annotated, the default position wins.
Validation errors¶
When a value violates a constraint or a required parameter is missing, Veloce
responds with a 422 and a structured error body.
The same validation rules apply across all seven marker types: numeric bounds
(ge, le, gt, lt, multiple_of) for numbers, and min_length /
max_length / pattern for strings.
See Error handling for customising the response.
Next steps¶
- Routing — path converters and marker-free parameter binding.
- Requests & responses — Pydantic body models.
- File uploads —
UploadFileand multipart forms. - Dependency injection — share parameter parsing
across handlers with
Depends. - API reference —
Query,Path,Body,Form,File,Header,Cookie.