Request and response schemas
Validate request bodies with Pydantic and describe response shapes in Swagger.
Work through each step in order. The examples use the generated LenAPI project structure and are designed to be adapted to your app.
Define the Student database model
The Pydantic schemas below describe API input and output; the ORM model defines the table where records are stored. Put Student in app/models.py, import it from main.py for migration discovery, and run the migration command before using these routes.
# app/models.py
from lenapi import model
class Student(model.Model):
person_name = model.StringField(max_length=120, unique=True)
course = model.StringField(max_length=120)
# Generate and apply the table migration:
# python lenora.py migrationDefine request and response schemas
Keep API schemas in app/schemas.py. Field constraints validate incoming JSON, while from_attributes lets the response schema read fields from the SQLAlchemy-backed Student instance.
# app/schemas.py
from pydantic import BaseModel, ConfigDict, Field
class StudentCreate(BaseModel):
person_name: str = Field(min_length=1, max_length=120)
course: str = Field(min_length=1, max_length=120)
class StudentOutput(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
person_name: str
course: strUse the schemas in routes and inspect Swagger
A Pydantic model parameter becomes the JSON request body. response_model documents and validates the response. The Student model performs the database operations, so the example is connected end to end.
# main.py
from lenapi import LENAPI
from app.models import Student
from app.schemas import StudentCreate, StudentOutput
app = LENAPI()
@app.post("/students", response_model=StudentOutput)
def create_student(data: StudentCreate):
return Student.create(
person_name=data.person_name,
course=data.course,
)
@app.get("/students/{student_id}", response_model=StudentOutput)
def get_student(student_id: int):
return Student.get_or_404(id=student_id)
@app.get("/students", response_model=list[StudentOutput])
def list_students(limit: int = 20, offset: int = 0):
return Student.all().all(limit=limit, offset=offset)Open /docs/: POST /students shows the StudentCreate JSON body, GET /students/{student_id} shows the integer path parameter, and GET /students shows limit and offset as query parameters. All three document StudentOutput responses.
Accept arbitrary webhook JSON when needed
If an external sender has a variable payload, use Body with Any to accept arbitrary JSON. Verify the sender separately with webhook authentication.
from typing import Any
from lenapi import Body
@app.post("/webhooks/events")
async def receive_event(payload: Any = Body()):
return payload