Skip to main content
>_ supraj.dev

Module 1: Engineering Foundations · 3.75h

01 · UNDERSTAND

Day 07 Theory — FastAPI Fundamentals: Serve the First AI Service

Why an API boundary matters

So far, much of our Service Desk logic can be called directly from Python. A production application needs a stable boundary that other systems can use.

An HTTP API gives us that boundary. A web client, mobile app, automation, or another service can send a request without needing to know how the internal Python code is organized.

FastAPI lets us expose typed Python application logic through HTTP while integrating naturally with Pydantic validation and OpenAPI documentation.

Separate transport from domain logic

One of today's most important architecture lessons is that a route handler should not become the whole application.

Bad shape:

HTTP route
 └─ validation + business rules + provider call + persistence + formatting

Better shape:

HTTP route
   ↓
application/service layer
   ↓
domain + integrations

The HTTP layer translates between network requests and application types. The underlying business behavior should still be testable without starting a web server.

HTTP methods communicate intent

Common methods include:

  • GET — retrieve a representation,
  • POST — submit/create/process something,
  • PUT — replace a resource representation,
  • PATCH — partially update,
  • DELETE — remove.

These are conventions with semantics that affect caching, clients, proxies, and retry decisions. Choose them deliberately.

Path, query and body inputs

A request can carry information in different places.

/tickets/123             -> path parameter
/tickets?status=open     -> query parameter
POST JSON body           -> request body
Authorization: Bearer... -> header

Use each for the role it is designed to play rather than placing every input in one JSON body.

Pydantic at the network boundary

Network input is untrusted.

A request model allows FastAPI to parse and validate input before domain logic proceeds.

class TicketRequest(BaseModel):
    title: str
    description: str

Validation is useful, but remember Day 04: schema validation is not authorization or business correctness.

Response models are contracts too

A response model documents and validates what our API promises to return.

This prevents accidental leakage of internal fields and makes generated OpenAPI documentation more useful.

Do not return raw database objects or provider payloads simply because they are convenient.

Status codes are part of the API contract

A useful API distinguishes outcomes.

Examples:

  • 200 OK — successful request,
  • 201 Created — resource created,
  • 400 Bad Request — request cannot be processed as submitted,
  • 401 Unauthorized — authentication is required or invalid,
  • 403 Forbidden — identity is known but not permitted,
  • 404 Not Found — requested resource does not exist,
  • 422 Unprocessable Content — commonly used by FastAPI for validation failures.

Choose responses according to your API contract rather than returning 200 with { "error": ... } for every failure.

Async route handlers

FastAPI supports async def route handlers, which is useful when the route waits on async database or network operations.

But Day 02 still applies: putting blocking work inside an async handler can block the event loop.

Async is an implementation choice based on the workload, not a badge every endpoint must have.

Dependency injection

FastAPI dependencies can supply shared concerns such as authentication context, database sessions, or configured services.

Dependency injection also improves testing because production integrations can be replaced with controlled test doubles.

OpenAPI is generated documentation, not complete product documentation

FastAPI can generate a machine-readable OpenAPI specification and interactive Swagger UI. This is very useful, but a real API still needs clear descriptions of semantics, authentication, error behavior, idempotency, and workflows.

Service Desk connection

Today the Service Desk becomes a service rather than only a Python program.

External callers should interact with a stable HTTP contract while our internal architecture remains free to evolve.

The principle is:

Keep the HTTP layer thin, validate untrusted input at the boundary, return deliberate contracts, and keep domain behavior independently testable.

02 · APPLY

Lesson Overview

This is the applied companion for Day 07. Read DAY_07_THEORY.md first for the beginner-first teaching of FastAPI Fundamentals: Serve the First AI Service. Then use the real service-desk-day-07/ project to trace, run, debug, and explain the concept.

Service Desk Alignment

Day 07 adds FastAPI Fundamentals: Serve the First AI Service to the running Service Desk. Start with models.py, api/routes_health.py, api/routes_tickets.py, main.py, exceptions.py, adapters/base.py, then follow imports and tests to identify the actual runtime path.

Why This Topic Matters

The theory chapter explains why FastAPI Fundamentals: Serve the First AI Service is needed. Here the goal is evidence: identify where the capability is implemented, what observable behavior changes, and how the repository proves both success and failure behavior.

System Architecture

graph TD
    A[POST /api/v1/triage] --> B[Pydantic Request Validation]
    B --> C[Path Operation Handler]
    C --> D[Pydantic Response Serialization]
    D --> E[HTTP 200 JSON Response]

Worked Code Example: FastAPI

from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
import uvicorn

app = FastAPI(title="AI Service Desk Triage API")

class TriageRequest(BaseModel):
    ticket_id: str
    issue_description: str

class TriageResponse(BaseModel):
    ticket_id: str
    category: str
    assigned_tier: int

@app.post("/api/v1/triage", response_model=TriageResponse)
async def triage_ticket(payload: TriageRequest):
    if not payload.issue_description.strip():
        raise HTTPException(status_code=400, detail="Description cannot be empty")
    
    # Deterministic Triage Logic
    category = "SECURITY" if "password" in payload.issue_description.lower() else "GENERAL"
    return TriageResponse(ticket_id=payload.ticket_id, category=category, assigned_tier=1)

Code Walkthrough & Mechanics

Read models.py, api/routes_health.py, api/routes_tickets.py, main.py, exceptions.py, adapters/base.py, api/dependencies.py, middleware/request_id.py, adapters/mock_adapter.py, services/ticket_service.py with these theory sections beside you:

  • Why an API boundary matters — locate its implementation and evidence.
  • Separate transport from domain logic — locate its implementation and evidence.
  • HTTP methods communicate intent — locate its implementation and evidence.
  • Path, query and body inputs — locate its implementation and evidence.
  • Pydantic at the network boundary — locate its implementation and evidence.

For each concept identify the real function/class/protocol boundary, its input/state, its observable result, and the assertion in tests/test_api.py, tests/test_middleware.py, tests/test_service.py that proves the behavior. If a concept has no implementation or evidence, record that as a gap rather than inventing one.

Common Mistakes & Debugging Guidance

Use the theory—not generic timeout or .env advice—to decide what can fail today.

Failure lens: revisit Path, query and body inputs, Pydantic at the network boundary.

  • Reproduce the smallest case that violates one of those expectations.
  • Trace the real Day 07 modules until you find the first incorrect state/output/decision.
  • Use tests/test_api.py, tests/test_middleware.py, tests/test_service.py as executable evidence.
  • Add a regression test if the failure is not already represented.
  • Fix the smallest responsible boundary and rerun the relevant test before the full suite.

Your debugging explanation must name the topic-specific invariant that failed, not merely say “an exception occurred.”

Practical Lab Instructions

  1. Summarize these theory ideas before opening the implementation:
    • Why an API boundary matters
    • Separate transport from domain logic
    • HTTP methods communicate intent
    • Path, query and body inputs
  2. Inspect the most relevant real Day 07 modules first:
    • service_desk/models.py
    • service_desk/api/routes_health.py
    • service_desk/api/routes_tickets.py
    • service_desk/main.py
    • service_desk/exceptions.py
    • service_desk/adapters/base.py
    • service_desk/api/dependencies.py
    • service_desk/middleware/request_id.py
    • service_desk/adapters/mock_adapter.py
    • service_desk/services/ticket_service.py
  3. Inspect the automated evidence:
    • tests/test_api.py
    • tests/test_middleware.py
    • tests/test_service.py
  4. Establish the baseline:
    cd service-desk-day-07
    PYTHONPATH=. pytest tests/test_api.py tests/test_middleware.py tests/test_service.py -q
    
  5. Trace one theory concept through the actual nested modules and tests.
  6. Run one success case and record input → mechanism → observable result.
  7. Exercise one topic-specific failure/boundary case and name the invariant that protects the system.
  8. Re-run the relevant tests and explain theory → implementation → evidence.

Done when: another student can reproduce your trace without relying on an invented sample.

Key Takeaways

  1. Be able to explain Why an API boundary matters and point to its implementation/evidence in Day 07.
  2. Be able to explain Separate transport from domain logic and point to its implementation/evidence in Day 07.
  3. Be able to explain HTTP methods communicate intent and point to its implementation/evidence in Day 07.

Knowledge Check & Scenario Questions

  1. Concept: Using Why an API boundary matters, explain the engineering problem Day 07 is solving without naming a framework as the answer.
  2. Mechanism: How does Separate transport from domain logic appear in the real project? Start from service_desk/models.py and name the observable state/output/event that changes.
  3. Failure: For HTTP methods communicate intent, describe one incorrect implementation or boundary condition and the evidence you would expect in tests/test_api.py.
  4. Design review: Which assumption in today's design would you verify before reusing this implementation in a different production system?

Official References

03 · EXPLAIN

Interview checkpoint

Explain one design decision from this lesson, the alternative you rejected, and the failure mode or evidence that justified your choice.