Pydantic Integration
Robyn supports Pydantic v2 as an optional dependency for automatic request body validation and rich OpenAPI schema generation. When Pydantic is not installed, there is zero performance overhead — Robyn never imports it.
Installation
Install Robyn with Pydantic support using the optional extra:
Installation
pip install "robyn[pydantic]"
Basic Usage
Define a Pydantic BaseModel and use it as a type annotation on your handler parameter. Robyn will automatically parse the incoming JSON body, validate it against the model, and inject the validated instance into your handler.
Basic Pydantic Validation
from pydantic import BaseModel
from robyn import Robyn
app = Robyn(__file__)
class UserCreate(BaseModel):
name: str
email: str
age: int
active: bool = True
@app.post("/users")
def create_user(user: UserCreate):
"""Create a new user"""
return {
"name": user.name,
"email": user.email,
"age": user.age,
"active": user.active,
}
if __name__ == "__main__":
app.start()
Validation Errors
When the request body fails validation, Robyn automatically returns a 422 Unprocessable Entity response with structured error details. You do not need to write any error handling code.
For example, sending {"name": "Alice", "email": "alice@example.com", "age": "not_a_number"} would produce:
{
"error": "Validation Error",
"detail": [
{
"type": "int_parsing",
"loc": ["age"],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "not_a_number"
}
]
}
Missing required fields are also caught:
{
"error": "Validation Error",
"detail": [
{
"type": "missing",
"loc": ["email"],
"msg": "Field required",
"input": {"name": "Alice", "age": 30}
}
]
}
Nested Models
Pydantic models can reference other models. Robyn handles nested validation automatically.
Nested Models
from pydantic import BaseModel
from robyn import Robyn
app = Robyn(__file__)
class Address(BaseModel):
street: str
city: str
zip_code: str
class UserWithAddress(BaseModel):
name: str
email: str
address: Address
@app.post("/users")
def create_user(data: UserWithAddress):
"""Create a user with an address"""
return {"name": data.name, "city": data.address.city}
If the nested address object is missing or malformed, Robyn returns a 422 with the full error path (e.g. ["address", "city"]).
Using with the Request Object
You can combine Pydantic parameters with the standard Request object in the same handler. This gives you access to headers, query params, and other request metadata alongside the validated body.
Pydantic + Request
from pydantic import BaseModel
from robyn import Robyn, Request
app = Robyn(__file__)
class UserCreate(BaseModel):
name: str
email: str
age: int
active: bool = True
@app.post("/users")
def create_user(request: Request, user: UserCreate):
"""Create a user — access both raw request and validated model"""
return {
"method": request.method,
"name": user.name,
"email": user.email,
}
OpenAPI Integration
When you use Pydantic models, Robyn automatically generates rich JSON Schema in your OpenAPI specification at /openapi.json. This includes:
- Property types —
string,integer,boolean, etc. - Required fields — fields without defaults are listed in
required - Default values — shown in the schema
- Nested models — referenced via
$refand placed incomponents/schemas
OpenAPI with Pydantic
from pydantic import BaseModel
from robyn import Robyn, Request
app = Robyn(__file__)
class Address(BaseModel):
street: str
city: str
zip_code: str
class UserWithAddress(BaseModel):
name: str
email: str
address: Address
@app.post("/users", openapi_tags=["Users"])
def create_user(request: Request, data: UserWithAddress) -> dict:
"""Create a user with a nested address"""
return {"name": data.name, "city": data.address.city}
The generated /openapi.json will contain:
{
"paths": {
"/users": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "title": "Name"},
"email": {"type": "string", "title": "Email"},
"address": {"$ref": "#/components/schemas/Address"}
},
"required": ["name", "email", "address"],
"title": "UserWithAddress"
}
}
}
}
}
}
},
"components": {
"schemas": {
"Address": {
"type": "object",
"properties": {
"street": {"type": "string", "title": "Street"},
"city": {"type": "string", "title": "City"},
"zip_code": {"type": "string", "title": "Zip Code"}
},
"required": ["street", "city", "zip_code"],
"title": "Address"
}
}
}
}
Pydantic vs Body
Robyn supports two approaches for typed request bodies. Choose the one that fits your needs:
| Feature | Body subclass | Pydantic BaseModel |
|---|---|---|
| Installation | Built-in | pip install "robyn[pydantic]" |
| Validation | No automatic validation | Full validation with detailed errors |
| Error responses | Manual | Automatic 422 with structured errors |
| OpenAPI schema | Basic type inference | Full JSON Schema (types, required, defaults, $ref) |
| Nested models | Supported (basic) | Supported (with $ref in OpenAPI) |
| Performance overhead | None | Only when Pydantic is installed and used |
Both approaches work with OpenAPI documentation. If you need validation, use Pydantic. If you just need OpenAPI schema hints without validation, Body is sufficient.
What's next?
Batman wondered about whether Robyn handlers can be dispatched to multiple processes.
Robyn showed him the way!
