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 typesstring, integer, boolean, etc.
  • Required fields — fields without defaults are listed in required
  • Default values — shown in the schema
  • Nested models — referenced via $ref and placed in components/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:

FeatureBody subclassPydantic BaseModel
InstallationBuilt-inpip install "robyn[pydantic]"
ValidationNo automatic validationFull validation with detailed errors
Error responsesManualAutomatic 422 with structured errors
OpenAPI schemaBasic type inferenceFull JSON Schema (types, required, defaults, $ref)
Nested modelsSupported (basic)Supported (with $ref in OpenAPI)
Performance overheadNoneOnly 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!

Multiprocess Execution