Skip to content

Handling Body Fields in Esmerald

In Esmerald, body fields are used to parse and validate incoming request bodies. The Body class in Esmerald allows you to define structured data within the body of a request. This guide will cover how to handle and validate body fields using Esmerald.

📂 Using the Body Class

The Body class in Esmerald is used to define fields in the body of an HTTP request. These fields can be validated and documented using Pydantic models. You can also specify the media type of the body content, such as application/json, application/x-www-form-urlencoded, or multipart/form-data.

Example: Simple Body Field

from pydantic import BaseModel
from esmerald import Esmerald, Gateway, JSONResponse, post, Body

class User(BaseModel):
    name: str
    email: str

@post("/create")
async def create_user(data: User = Body(...)) -> JSONResponse:
    """
    Creates a new user based on the request body.
    """
    return JSONResponse({"message": "User created", "user": data.model_dump()})

app = Esmerald(routes=[Gateway(handler=create_user)])

Explanation:

  • The User Pydantic model defines the structure of the incoming data.
  • The data parameter is annotated with Body(...), indicating that the body of the request should be parsed into the User model.
  • The create_user endpoint accepts the user data in JSON format and returns the same data in the response.

🌐 Setting the media_type for Body Fields

Esmerald allows you to define the media_type for body fields, specifying the format of the data being sent.

Available Media Types:

  • application/json (default)
  • application/x-www-form-urlencoded
  • multipart/form-data

You can define the media_type when using the Body decorator to specify how Esmerald should parse the incoming request body.

Example: Body with media_type

from pydantic import BaseModel
from esmerald import Esmerald, Gateway, JSONResponse, post, Body
from esmerald.utils.enums import EncodingType

class User(BaseModel):
    name: str
    email: str

@post("/create")
async def create_user(
    data: User = Body(..., media_type=EncodingType.URL_ENCODED)
) -> JSONResponse:
    """
    Creates a user with the body sent as x-www-form-urlencoded.
    """
    return JSONResponse({"message": "User created", "user": data.model_dump()})

app = Esmerald(routes=[Gateway(handler=create_user)])

Explanation:

  • The media_type=EncodingType.URL_ENCODED specifies that the body content should be parsed as application/x-www-form-urlencoded, commonly used in forms.
  • This allows you to work with URL-encoded form data instead of JSON.

🧩 Using Body with Pydantic Models

Esmerald leverages Pydantic for automatic data validation and serialization. You can define more complex data structures using Pydantic models and use them as body fields.

Example: Using Body with Nested Pydantic Models

from pydantic import BaseModel
from esmerald import Esmerald, Gateway, JSONResponse, post, Body

class Address(BaseModel):
    street: str
    city: str
    country: str

class User(BaseModel):
    name: str
    email: str
    address: Address

@post("/create")
async def create_user(data: User = Body(...)) -> JSONResponse:
    """
    Creates a user with a nested address.
    """
    return JSONResponse({"message": "User created", "user": data.model_dump()})

app = Esmerald(routes=[Gateway(handler=create_user)])

Explanation:

  • The User model contains a nested Address model. The data parameter in the create_user function is annotated as Body(...), which will automatically parse the incoming request body into the nested User and Address models.
  • This allows you to work with complex, nested data structures while keeping the validation and parsing automatic.

📝 Optional and Default Fields in Body

In Esmerald, body fields can be optional or have default values. You can use Python's typing and Pydantic features to define whether a field is required or optional.

Example: Optional Fields in Body

from typing import Optional
from pydantic import BaseModel
from esmerald import Esmerald, Gateway, JSONResponse, post, Body

class User(BaseModel):
    name: str
    email: Optional[str] = None  # Optional field

@post("/create")
async def create_user(data: User = Body(...)) -> JSONResponse:
    """
    Creates a user, with an optional email.
    """
    return JSONResponse({"message": "User created", "user": data.model_dump()})

app = Esmerald(routes=[Gateway(handler=create_user)])

Explanation:

  • The email field is marked as Optional[str], meaning it is not required in the request body. If the email is not provided, it will default to None.
  • This allows you to handle flexible form submissions where some fields may be omitted.

⚙️ Advanced Field Validation in Body

You can apply advanced validation to body fields using Pydantic's Field class, which provides attributes like min_length, max_length, and custom validation.

Example: Using Field for Validation

from pydantic import BaseModel, Field
from esmerald import Esmerald, Gateway, JSONResponse, post, Body

class User(BaseModel):
    name: str = Field(..., min_length=3)  # Ensures the name has at least 3 characters
    email: str = Field(..., regex=r"^[\w\.-]+@[\w\.-]+\.\w{2,3}$")  # Email validation

@post("/create")
async def create_user(data: User = Body(...)) -> JSONResponse:
    """
    Creates a user with field validation.
    """
    return JSONResponse({"message": "User created", "user": data.model_dump()})

app = Esmerald(routes=[Gateway(handler=create_user)])

Explanation:

  • The Field(..., min_length=3) ensures that the name field has at least three characters.
  • The Field(..., regex=r"^[\w\.-]+@[\w\.-]+\.\w{2,3}$") validates that the email field is in a correct email format.
  • These validations ensure that the request data meets specific requirements before reaching your handler logic.

📂 Summary

  • Body allows you to handle and validate request bodies in Esmerald, using Pydantic models or simple Python data structures.
  • You can customize the body content using media_type, choosing between application/json, application/x-www-form-urlencoded, and multipart/form-data.
  • Esmerald makes it easy to handle complex, nested models and apply custom validations using Pydantic’s powerful features.
  • Optional fields and default values allow you to create flexible API endpoints that can handle a variety of input scenarios.