FastAPI Compiler

Define routes and call them. A real FastAPI app running in your browser - no server, no install, nothing sent anywhere.

← VizLearn
main.py FastAPI
Output

                

What this is

FastAPI 0.115 on Pydantic 2.7 and CPython 3.12, all compiled to WebAssembly and running on your own machine. Nothing is uploaded and no server is involved.

The first Run takes a few seconds while the interpreter and the library download. After that it is immediate.

Where is the server?

There isn't one, and there cannot be: a browser tab cannot listen on a port. But a server is not what makes FastAPI work.

Underneath, a FastAPI app is one async function that takes a request as a dictionary and sends a response back as messages. That interface is called ASGI, and uvicorn's whole job is to translate real network traffic into it. Skip the network and call the app directly and everything above that line — routing, type coercion, validation, status codes, the OpenAPI schema — behaves exactly as it does in production, because it is the same code.

That is what client does. It is defined for you before your code runs, and it has the shape of fastapi.testclient.TestClient, so what you write here is what you would write in a test file.

What works

  • Path and query parameters, with the coercion and the 422 that follow from the annotations.
  • Pydantic request bodies and response_model.
  • HTTPException, status_code, custom headers, Depends.
  • APIRouter, middleware, exception handlers.
  • app.openapi() — the schema that becomes the interactive docs.
  • Both def and async def endpoints.

What does not

  • uvicorn.run(...) and anything else that needs a socket. Use client instead.
  • Real I/O inside an endpoint — a network call, an asyncio.sleep, a database driver. There is no event loop to suspend into, and you will get a clear error rather than a hang.
  • The /docs page, which needs a browser pointed at a running server. app.openapi() gives you the schema behind it.
  • WebSockets, background tasks that outlive the request, and file uploads — python-multipart is not loaded.

One honest difference

In a real server a def endpoint is handed to a thread pool so it cannot block the event loop, while an async def one runs on the loop itself. That distinction is most of what "should this be async?" is about.

This runtime cannot start threads, so def endpoints are run inline here. Everything you can observe — the response, the status, the validation — is identical, and the difference only shows up in a blocking call, which cannot happen here anyway. It is worth knowing about before you reason about performance from what you see on this page.

Reading a 422

422 is FastAPI telling you the request never reached your function. The body it returns is a Pydantic error list, and the useful field is loc — the path to the thing it rejected.

["path", "book_id"] means the URL, and ["query", "verbose"] means the query string. Anything starting ["body", ...] is inside the JSON you sent, with the rest of the list naming the field — so ["body", "price"] is one level in. A 422 you did not expect is nearly always an annotation promising something the caller did not send.

Things worth trying

  • Add response_model=Book to a route, then return a dict with an extra key and see it filtered out.
  • Write a dependency with Depends and use it in two routes.
  • Print app.openapi() in full and find the constraint you wrote with Field(gt=0).
  • Change a parameter's annotation from int to str and watch which requests start passing.
  • Add a second router with APIRouter(prefix="/v2") and include it.

The layer below

Most of what FastAPI does with your annotations is Pydantic doing it. The Pydantic compiler is the same editor with just that library, and the Python compiler is the language on its own.