Skip to content

Debugging FastAPI in VSCode

Print-debugging through a request flow misses things. By the time print(payload) shows up in your terminal, the request has already been parsed, validated, queried against the database, and partially serialized. A real debugger lets you pause at any of those points and look around.

VSCode ships a Python debugger that works well with FastAPI.

Configuring launch.json

VSCode launches the debugger using a config file at .vscode/launch.json. Create the file if it doesn't exist, and add this configuration:

.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "FastAPI: debug",
            "type": "debugpy",
            "request": "launch",
            "module": "uvicorn",
            "args": ["release_tracker.main:app", "--port", "8001"],
            "cwd": "${workspaceFolder}/src",
            "console": "integratedTerminal"
        }
    ]
}
  • It launches uvicorn directly instead of fastapi dev. The dev wrapper uses subprocess-based hot reload, and breakpoints don't fire reliably inside subprocesses. Plain uvicorn runs in the same process as the debugger.
  • The debug server listens on port 8001 so it doesn't conflict with uv run fastapi dev (which uses 8000).

You can keep the regular dev server running on 8000 while a debug session runs alongside on 8001.

  • cwd is set to ${workspaceFolder}/src so the release_tracker package resolves correctly.
  • integratedTerminal keeps logs visible in VSCode's terminal panel instead of the debug console, which is easier to read.

To start the debugger, open the Run and Debug view (the bug-and-play icon in the activity bar), pick FastAPI: debug from the dropdown, and press the green play button (or F5).

What you give up: hot reload

This config trades hot reload for working breakpoints. When you change a route handler, you'll need to stop and restart the debugger. For everyday iteration, keep using uv run fastapi dev. For stepping through a specific bug, use this config.

Setting a Breakpoint

Open src/release_tracker/main.py and click in the gutter just to the left of the line number on this line in get_project:

Python
project = session.get(Project, project_id)

A red dot appears. That's a breakpoint. With the debugger running, request GET /projects/1 from the docs page (http://127.0.0.1:8001/docs). Execution pauses at the breakpoint before session.get fires.

Inspecting Variables

VSCode's left panel shows debugger views while paused:

  • Variables:

    The current frame's locals. project_id is already populated from the path parameter; session is the injected SQLModel Session.

  • Watch:

    Expressions you want to evaluate continuously. Add session.bind.url to see which database the session is connected to.

  • Call Stack:

    The chain of frames that got you here. The top is get_project. Below it are FastAPI's request-dispatch frames. You can click any frame to jump to its locals.

Hover over any variable in the editor to see its current value. For complex objects (like session), expand it in the Variables panel to drill into attributes.

Stepping Through

Debug controls live at the top of the editor while paused:

  • Continue (F5):

    Run until the next breakpoint or the request finishes.

  • Step Over (F10):

    Execute the current line and pause on the next one in the same function.

  • Step Into (F11):

    Descend into the function being called on the current line.

  • Step Out (Shift+F11):

    Finish the current function and pause in the caller.

  • Restart / Stop:

    Restart or kill the debug session.

For the route handler above, F10 advances past session.get(...) and lets you inspect the resulting project before the if not project check fires.

Where This Stops

This is enough to get unblocked on most route-handler bugs. VSCode's debugger has a lot more to it: conditional breakpoints, log points (no-pause logging), watch expressions that mutate state, evaluate expressions in the debug console, attach to a running process. When you're ready, the VSCode Python debugging docs walk through the rest.