First FastAPI App

beginner fastapi uvicorn swagger redoc

Let’s get a FastAPI app actually running. Three steps: install, write a file, run uvicorn.

Install

pip install "fastapi[standard]"

The [standard] extra pulls in uvicorn and a few useful goodies (httpx for the test client, email-validator, etc.). If we want bare-bones we can do pip install fastapi uvicorn instead.

The smallest app

Create main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello from FastAPI"}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id, "name": f"Item #{item_id}"}

A few things to notice:

  • app = FastAPI() creates the application instance. Everything attaches to this.
  • @app.get("/") registers a GET handler at /. Same pattern as Flask but on the app instance.
  • The function can be async def or plain def. FastAPI handles both — plain def runs in a threadpool so we don’t block the event loop.
  • The dict we return gets serialized to JSON automatically.

Run it

uvicorn main:app --reload

Breaking down main:appmain is the Python module (the file main.py), app is the variable name inside that file. --reload watches files and restarts on change. Only use it in dev.

Output should look like:

INFO:     Uvicorn running on http://127.0.0.1:8000
INFO:     Started reloader process
INFO:     Application startup complete.

Hit http://localhost:8000/ in a browser and we get {"message": "Hello from FastAPI"}.

The free docs

This is the magic. Without writing any extra code, go to:

  • http://localhost:8000/docs — interactive Swagger UI. We can expand each endpoint and hit “Try it out” to fire real requests.
  • http://localhost:8000/redoc — ReDoc-style docs (cleaner, three-column layout, better for reading).
  • http://localhost:8000/openapi.json — the raw OpenAPI 3.1 spec as JSON. Useful for client codegen.
Request lifecycle
1. Browser → GET /items/42
2. Uvicorn parses HTTP, calls ASGI app
3. FastAPI matches route → read_item
4. Validates item_id is int
5. Calls handler, serializes dict → JSON
6. Uvicorn writes HTTP response

Test it from the terminal

curl http://localhost:8000/items/42
# {"item_id":42,"name":"Item #42"}

curl http://localhost:8000/items/abc
# {"detail":[{"type":"int_parsing","loc":["path","item_id"],...}]}

That second response is FastAPI rejecting abc because we typed item_id as int. We got validation for free.

App-level options

FastAPI() accepts a bunch of kwargs that show up in the docs:

app = FastAPI(
    title="Inventory Service",
    description="Manages items, stock levels, and reorders.",
    version="0.1.0",
    docs_url="/swagger",     # change default /docs path
    redoc_url=None,          # disable redoc
)

In prod we usually set docs_url=None and redoc_url=None to hide internal docs from the public.

fastapi dev (newer way)

Recent FastAPI versions ship a CLI:

fastapi dev main.py

Same thing as uvicorn main:app --reload but with nicer output. For prod use fastapi run main.py which skips the reloader.

That’s it — we have a working API, free docs, and validation. The rest is just adding more routes.