Build a Typed FastAPI API with Pydantic
🚀 Technical Briefing: This tutorial is part of our deep-dive series on Agentic Workflows at Gate of AI. For the full technical breakdown, interactive code sandbox, and the native Arabic translation, visit the original ar
🚀 Technical Briefing: This tutorial is part of our deep-dive series on Agentic Workflows at Gate of AI. For the full technical breakdown, interactive code sandbox, and the native Arabic translation, visit the original article here.
Tutorial | Intermediate
Build a Typed FastAPI Service with Pydantic
This guide builds a small typed HTTP service using FastAPI and Pydantic. It demonstrates request validation, a stable response contract, and the framework's route-first API structure. It does not claim a verified OpenAI or Anthropic integration: the supplied reference material does not establish provider SDK calls, model identifiers, retry policies, or cross-provider failover behavior.
What this tutorial covers
FastAPI is described in the verified material as a modern Python framework for building APIs. The referenced reviews highlight its asynchronous capabilities, Pydantic validation, and automatic interactive API documentation. Those are the capabilities this example uses: a route receives a typed request, validates it through a Pydantic model, and returns a typed response documented by the framework.
The example uses a support-ticket classification-shaped contract, but it deliberately does not call a language model. That distinction matters. A validated HTTP API is not, by itself, an AI router. To route requests to OpenAI or Anthropic, an implementation also needs provider-specific clients and verified configuration, response handling, error behavior, and policy decisions. Those details cannot be responsibly supplied from the available sources here.
The verified context also describes a wider architectural distinction. FastAPI is route-first: an HTTP route declaration is central to registering an endpoint and its schema. If the same capability must also be exposed as an MCP tool, the context says a separate registration may be needed, with independently maintained declarations. HarnessAPI's research describes a skill-first alternative that projects a skill and its type contracts onto transports. This tutorial stays with ordinary FastAPI routes and does not implement MCP or HarnessAPI.
Prerequisites
- Python and familiarity with functions, type annotations, and JSON.
- A terminal where you can install Python packages and run a local development server.
- Basic understanding of HTTP requests and responses.
No model API key is required because the sample does not make provider calls. Package versions and provider model identifiers are not specified by the verified context, so this guide avoids claiming specific compatible version floors.
Step 1: Create a project and install the framework
Create a project directory and a virtual environment. Install FastAPI and an ASGI server using your normal Python package workflow. For a shared or deployed project, choose and lock versions that your team has tested; the supplied sources do not verify particular current version numbers.
mkdir typed-fastapi-service
cd typed-fastapi-service
python -m venv .venv
macOS or Linux
source .venv/bin/activate
Windows PowerShell
.venv\Scripts\Activate.ps1
python -m pip install fastapi uvicorn
Create a file named main.py in the project directory. FastAPI uses route declarations to associate HTTP methods and paths with Python endpoint functions. The function's type annotations and Pydantic models can describe the data expected by the endpoint.
Step 2: Define typed request and response models
Define the data contract before writing the route. In this example, the request includes a subject and message. The response is a simple acknowledgement. The sample intentionally avoids claiming that a model has classified the ticket: the endpoint has not called a model and should not misrepresent its output as AI-generated analysis.
from pydantic import BaseModel, Field
class TicketRequest(BaseModel):
subject: str = Field(min_length=3, max_length=200)
body: str = Field(min_length=10, max_length=8000)
class TicketAccepted(BaseModel):
status: str
subject: str
The model declarations make the intended input and output shapes visible in Python. The constraints define minimum and maximum lengths for the two request fields. Pydantic validation is the part of this pattern supported by the provided context: the HarnessAPI description specifically discusses using Pydantic input-model validation and returning an HTTP 422 response when validation fails.
These models are application contracts, not instructions to a language model. They do not ensure that external model output is safe or correct. If a later implementation calls a model, its response must be treated as external data and validated against an appropriate output contract before the application relies on it. The supplied context does not provide enough information to prescribe provider-specific response parsing.
Step 3: Add a route-first FastAPI endpoint
Use a route decorator to register the endpoint and connect the request model to the function. Return the declared response model. This illustrates the route-first structure described in the verified research: route registration is the organizing unit for the HTTP service.
from fastapi import FastAPI
from main_models import TicketAccepted, TicketRequest
app = FastAPI(
title="Typed Ticket API",
description="A small API example using typed request and response models.",
version="1.0.0",
)
@app.post("/tickets", response_model=TicketAccepted)
async def accept_ticket(ticket: TicketRequest) -> TicketAccepted:
return TicketAccepted(
status="accepted",
subject=ticket.subject,
)
For this compact example, keep the models and application in one file rather than importing them from a second module. Replace the preceding code with this complete single-file version as main.py:
from fastapi import FastAPI
from pydantic import BaseModel, Field
class TicketRequest(BaseModel):
subject: str = Field(min_length=3, max_length=200)
body: str = Field(min_length=10, max_length=8000)
class TicketAccepted(BaseModel):
status: str
subject: str
app = FastAPI(
title="Typed Ticket API",
description="A small API example using typed request and response models.",
version="1.0.0",
)
@app.post("/tickets", response_model=TicketAccepted)
async def accept_ticket(ticket: TicketRequest) -> TicketAccepted:
return TicketAccepted(
status="accepted",
subject=ticket.subject,
)
The route accepts a TicketRequest, not an arbitrary dictionary. FastAPI can use the model and endpoint declaration to describe the HTTP interface. The verified reviews specifically call out automatic interactive documentation and OpenAPI generation as useful FastAPI features. The example makes those generated descriptions available through the framework's documentation interface; it does not claim that the documentation substitutes for authentication or operational controls.
Run the service locally:
uvicorn main:app --reload
Then open the local documentation page at /docs on the development server. Use it to inspect the endpoint's request and response shapes and try a request. Interactive docs are a development convenience and a representation of the declared API contract; they do not add an AI provider or business behavior that the endpoint does not implement.
Step 4: Exercise validation and inspect the response
Send a request whose values satisfy the declared constraints. For example:
curl --request POST "http://127.0.0.1:8000/tickets" \
--header "Content-Type: application/json" \
--data '{
"subject": "Cannot sign in after password reset",
"body": "The reset link worked, but the sign-in page rejects the new password."
}'
The endpoint returns an acknowledgement containing the subject. It does not assign a category, score urgency, generate a reply, or contact a model provider. If a required field is missing or violates a declared constraint, the request-model validation path rejects the input. The HarnessAPI source describes Pydantic validation errors being returned as HTTP 422 in its routing design; this example relies on FastAPI's model-based request handling rather than implementing that separate framework's endpoint.
Try changing the subject to a value shorter than the minimum or removing the body field. Inspect the response and the generated API documentation. This is a useful way to check that the declared schema is connected to the actual route instead of existing only as an unused Python class.
Where a provider router would fit
A real AI router needs an additional layer beyond this typed HTTP boundary. At a minimum, a design must define how provider configuration is supplied, which provider is selected, how provider responses are converted into an application-owned output shape, and how failures are represented to callers. It also needs tested provider SDK calls and model identifiers. None of those OpenAI- or Anthropic-specific facts are established by the verified material supplied for this audit, so this guide does not invent them or label this example a functioning dual-provider router.
Keep provider behavior separate from the FastAPI route when adding a verified implementation. The route should express the HTTP contract; a distinct component can own a provider call and translate its result into application types. Validate any returned structured data before using it. The boundary between a declared Python schema and an external model response should be explicit, rather than assuming generated text automatically satisfies the application's requirements.
The verified HarnessAPI paper offers a related architectural point. In route-first designs, adding a second transport such as MCP can involve a second registration in a different vocabulary, and the paper notes that separately declared schemas can drift when a Pydantic model changes. Its skill-first proposal instead treats the typed skill as the central unit and projects it onto transports. That is a research-described approach, not a feature implemented by this FastAPI sample. Choose an architecture based on the transports the application actually needs, and verify the behavior of the framework you adopt.
The supplied reporting on PydanticAI describes a model-agnostic agent framework and highlights Python-native control, type safety, and Logfire integration. However, that reporting says Anthropic support was planned in its described context. It should not be read as proof of current Anthropic compatibility or as a substitute for checking current official documentation before selecting a provider stack.
Common mistakes to avoid
- Calling a typed endpoint an AI router. This sample validates and acknowledges a request; it does not make a model call.
- Assuming a schema validates every downstream value. The request model covers the incoming HTTP data. Any external response needs its own suitable validation boundary.
- Confusing OpenAPI documentation with implementation. Generated documentation describes declared routes and models; it does not implement provider access or operational policy.
- Duplicating transport contracts without considering drift. The verified HarnessAPI research identifies separately maintained HTTP and MCP declarations as a maintenance concern and describes a skill-first alternative.
- Assuming provider support from a broad framework description. Check the current provider documentation and tested SDK behavior before claiming compatibility.
Key takeaways
FastAPI's route-first model gives a Python endpoint a clear HTTP registration point. Pydantic models describe typed inputs and outputs, and FastAPI can generate interactive API documentation from the declared interface. The verified context supports those fundamentals and the importance of Pydantic validation; it does not verify the draft's specific dual-provider code, retry implementation, failover policy, or production claims.
Use this example as a typed API foundation, not as a finished AI router. Before adding OpenAI or Anthropic, verify the current SDK documentation and model availability, implement and test provider-specific adapters, define explicit error and data-handling behavior, and validate model output against an application-owned schema. If the service also needs MCP exposure, account for the separate-registration maintenance issue identified in the verified research and evaluate whether a skill-first design is appropriate.
Editorial review by the Gate of AI Editorial & Engineering Teams. Gate of AI operates under GateOfAI, LLC, a Delaware, USA entity.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.