ESC

Type to search...

Body Multiple Parameters

Handle multiple body fields, and combine them with path and query parameters.

@route Style

Body fields are matched to function parameters by name:

from evoid.web.route import Service, post

app = Service("api")

@app.post("/users")
async def create_user(name: str, email: str, age: int, role: str = "viewer") -> dict:
    return {
        "status": "created",
        "name": name,
        "email": email,
        "age": age,
        "role": role,
    }
curl -X POST http://localhost:8000/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "alice@example.com", "age": 30}'

# {"status": "created", "name": "Alice", "email": "alice@example.com", "age": 30, "role": "viewer"}

Combining Path + Body

Path parameters and body fields are matched separately — EVOID detects them by source:

@app.put("/users/{user_id}")
async def update_user(user_id: int, name: str, email: str, bio: str | None = None) -> dict:
    # user_id comes from the URL path
    # name, email, bio come from the request body
    result = {"id": user_id, "name": name, "email": email}
    if bio:
        result["bio"] = bio
    return result
curl -X PUT http://localhost:8000/users/42 \
  -H "Content-Type: application/json" \
  -d '{"name": "Bob", "email": "bob@example.com", "bio": "Developer"}'

Combining Path + Query + Body

All three sources work together:

@app.post("/users/{org_id}/invite")
async def invite_user(
    org_id: int,          # path parameter
    role: str = "viewer", # query parameter
    name: str = "",       # body field
    email: str = "",      # body field
) -> dict:
    return {
        "org_id": org_id,
        "name": name,
        "email": email,
        "role": role,
    }
curl -X POST "http://localhost:8000/users/5/invite?role=admin" \
  -H "Content-Type: application/json" \
  -d '{"name": "Eve", "email": "eve@example.com"}'

@controller Style

Same pattern in controllers:

from evoid.web.controller import Service, Controller, POST

app = Service("api")

@Controller("/orders")
class OrderController:
    @POST("/items/{item_id}")
    async def add_item(
        self, item_id: int, quantity: int, color: str = "white"
    ) -> dict:
        return {
            "item_id": item_id,
            "quantity": quantity,
            "color": color,
        }

Native Style

With native IOP, body data comes from intent.metadata['body']. Access multiple fields from the dict:

from evoid import Intent, Level, add_intent

CREATE_USER = Intent(
    name="create_user",
    level=Level.STANDARD,
    metadata={"method": "POST", "path": "/users/{org_id}"},
)

async def handle_create_user(intent: Intent) -> dict:
    org_id = intent.metadata.get("org_id")
    body = intent.metadata.get("body", {})

    name = body.get("name")
    email = body.get("email")
    role = body.get("role", "viewer")

    return {
        "org_id": org_id,
        "name": name,
        "email": email,
        "role": role,
    }

add_intent(CREATE_USER, handle_create_user)

Typed Body (Pydantic Models)

For complex bodies, define a Pydantic model:

from pydantic import BaseModel
from evoid.web.route import Service, post

class UserCreate(BaseModel):
    name: str
    email: str
    age: int
    role: str = "viewer"

app = Service("api")

@app.post("/users")
async def create_user(user: UserCreate) -> dict:
    return {
        "status": "created",
        "name": user.name,
        "email": user.email,
        "age": user.age,
        "role": user.role,
    }

This validates the full body structure and provides type hints and IDE autocompletion.