Serializers
Serializers are DRF-style input/output adapters built on Pydantic. They validate incoming JSON and produce JSON-safe dicts for outgoing responses.
from axion.serializer import Serializer, SerializerValidationError
Output: serializing database rows
Declare a serializer and build it from a row:
class AddressSerializer(Serializer):
street: str
city: str
postal_code: str
class UserSerializer(Serializer):
id: int
name: str
email: str
address: AddressSerializer
from services import get_user
user = get_user(1)
data = UserSerializer.from_row(user).data
response.json(data)
from_row(row)builds from an ORM object or attribute-mapping (uses Pydantic’sfrom_attributesmode).from_queryset(rows)builds a list from an iterable of rows..datareturns the JSON-safe representation; nested serializers are handled recursively.
Use it from a route with dependency injection:
@app.route("/api/users/<int:user_id>")
def get_user(user_id: int, session: Session, response: Response):
user = session.get(User, user_id)
if user is None:
response.send("User not found", 404)
return
response.json(UserSerializer.from_row(user).data)
Input: validating request bodies
Build a serializer from raw input and validate it:
class CreateUserSerializer(Serializer):
name: str
email: str
age: int
def validate_age(self, age: int) -> int:
if age < 18:
raise SerializerValidationError("Must be at least 18")
return age
@app.route("/api/users", methods=["POST"])
def create_user(request: Request, response: Response):
serializer = CreateUserSerializer.from_data(request.json)
if not serializer.is_valid():
response.json(serializer.errors, 400)
return
# serializer.validated_data is safe to use
response.json(serializer.validated_data, 201)
from_data(data)constructs from a mapping (the parsed JSON body).is_valid()runs the validation hooks; passraise_exception=Trueto raiseSerializerValidationErrorinstead of collecting errors.errorsmaps field names to messages (or nested dicts for nested serializers).validated_data/to_internal_value()return validated, JSON-safe values.to_representation()(and.data) return the serialized form.
Validation hooks
- Per-field hook:
validate_<field_name>(self, value)— raiseSerializerValidationErrorto record an error, or return a normalized value to replace the field. - Cross-field hook:
validate_serializer(self, data)— raiseSerializerValidationError; recorded undernon_field_errors.
Nested serializers are validated recursively; field errors propagate into the
parent’s errors under the nested field name.
Using serializers as handler parameters
Pydantic models (and serializers) can be declared directly as handler
parameters. axion validates the JSON body against them and returns 400
automatically on failure:
from pydantic import BaseModel
from axion import WebServer
from axion.http import Response
app = WebServer(debug=True)
class CreateUser(BaseModel):
name: str
email: str
@app.route("/api/users", methods=["POST"])
def create_user(user: CreateUser, response: Response):
response.json({"created": user.name}, 201)
Next steps
- Persist the validated data with the database guide.
- See the example app’s
serializers.pyfor a nested input/output flow with a real SQLite backend.