Skip to content

FastAPI Basics

We started with uv init which created a minimal project structure. For our web application we want something more: a release_tracker package installed into the virtual environment so tools like fastapi dev, pytest, and deployment systems can resolve our app through a dotted import path (release_tracker.main:app) instead of file paths.

Setting Up the Project

Create the Project

From the parent of your current project directory, run:

shell
cd ..
uv init --package --python "==3.14.*" release_tracker
cd release_tracker
code .

Info

code . opens VS Code on the new project directory. Use the fresh window for the rest of the course so the editor's workspace root matches the project root.

The --package flag tells uv to scaffold this as an installable Python package. You'll get:

  • src/release_tracker/ with an __init__.py (the source directory)
  • pyproject.toml (now including a [build-system] block)
  • README.md and .python-version
  • a git repository initialized with a .gitignore

Note

Using the --python "==3.14.*" flag pins the project to Python 3.14. At the time of this course, psycopg[binary] (the PostgreSQL driver we'll add later) doesn't support Python 3.15 or later yet. Keep an eye on the psycopg news page for the latest supported version, and bump your Python version.

Clean Up the Scaffolded Files

uv init --package assumes you're building a CLI tool, so it adds a [project.scripts] entry to pyproject.toml and a placeholder main() function in src/release_tracker/__init__.py. We're building a web app, not a CLI, so we'll clear both out.

Open pyproject.toml and delete this block:

TOML
[project.scripts]
release-tracker = "release_tracker:main"

Then empty out the __init__.py:

shell
rm src/release_tracker/__init__.py
touch src/release_tracker/__init__.py

The empty __init__.py still marks release_tracker as a Python package, so once we add main.py we can write from release_tracker.main import app.

Add the Ruff Config

Since this a new project directory, add a ruff.toml in the project root so the extension picks up our formatting and lint rules:

ruff.toml
line-length = 80
extend-exclude = ["alembic/versions"]

[lint]
select = ["E", "F", "I"]

Install FastAPI

Add FastAPI as a dependency:

shell
uv add 'fastapi[standard]>=0.115.0'

The [standard] extra pulls in the optional packages we'll want for development, including the Uvicorn server, the fastapi CLI, and httpx (which we'll use later for testing).

Write Your First FastAPI App

Create src/release_tracker/main.py and add the following code:

src/release_tracker/main.py
from fastapi import FastAPI

app = FastAPI(title="Release Tracker API")


@app.get("/projects")
def list_projects() -> list[dict]:
    return [{"id": 1, "name": "Example"}]

Breaking It Down

  1. app = FastAPI(...):

    This creates the core application instance. You'll use this app object to register routes, middleware, and event handlers.

  2. @app.get(...):

    This is a decorator. It tells FastAPI that the function immediately below it is in charge of handling HTTP GET requests that go to that specific URL path.

  3. Type Hints:

    Notice -> dict[str, str]. FastAPI heavily relies on your type hints to understand what you are returning.

  4. Returning Dictionaries:

    FastAPI automatically converts Python dictionaries into valid JSON responses.

Run the Server

To run our application, we need an ASGI (Asynchronous Server Gateway Interface) server. FastAPI is just a web framework. It doesn't actually bind to a port and listen for network traffic on its own.

Fortunately, fastapi[standard] comes with a powerful CLI and bundles a server called Uvicorn for us!

Run the following command in your terminal:

shell
uv run fastapi dev
  • uv run: Ensures we are executing the command using the virtual environment managed by uv.
  • fastapi dev: Starts the FastAPI development server, which automatically reloads whenever you save a change to a Python file.

How fastapi dev finds the app

fastapi dev with no arguments works because pyproject.toml includes a [tool.fastapi] block pointing at the application:

TOML
[tool.fastapi]
entrypoint = "release_tracker.main:app"

If you haven't configured an entrypoint, pass the path to the module instead:

shell
uv run fastapi dev src/release_tracker/main.py

If you navigate to http://127.0.0.1:8000/projects in your browser, you should see the JSON response returned by your list_projects function!

Understanding pyproject.toml

The pyproject.toml file uv has been managing for us is the modern standard (defined in PEP 621) for configuring Python projects, replacing older files you might have seen in legacy codebases like setup.py, setup.cfg, or requirements.txt.

After the commands above, your pyproject.toml should look like this:

TOML
[project]
name = "release-tracker"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
    { name = "Your Name", email = "you@example.com" }
]
requires-python = "==3.14.*"
dependencies = [
    "fastapi[standard]>=0.115.0",
]

[build-system]
requires = ["uv_build>=0.11.8,<0.12.0"]
build-backend = "uv_build"

[tool.uv.build-backend]
module-name = "release_tracker"
module-root = "src"

[tool.fastapi]
entrypoint = "release_tracker.main:app"

The description field is still the placeholder text uv init left behind. Update it to something meaningful for this project, like "A release tracking application to track Projects and Tasks".

Key Sections

  1. [project]:

    This block defines the metadata for your application (name, version, supported Python versions).

  2. dependencies:

    This is where uv records the packages your application needs to run. By declaring fastapi[standard] here, any developer who clones your repository can simply run uv sync to install exactly what they need.

  3. [build-system]:

    This tells uv how to install your project as a package, so release_tracker is importable from anywhere in your virtual environment.

  4. [tool.*]:

    Various Python tools look for their configuration under [tool.<name>] blocks in this file. Two are already in play: [tool.uv.build-backend] tells uv where the package source lives, and [tool.fastapi] is the entrypoint the fastapi dev command.

Note

Don't edit pyproject.toml to add packages, but you may need to edit it directly to add configuration settings.

Why the src/ Layout

Info

We create code under src/release_tracker to keep source code separate from configuration, tests, and scripts.


Simplifying the FastAPI Command

Typing out src/release_tracker/main.py every time you want to start the server gets tedious.

We can configure the fastapi CLI to know where our application lives. Add this block to pyproject.toml:

pyproject.toml
[tool.fastapi]
entrypoint = "release_tracker.main:app"

Stop your server (Ctrl+C) and start it again using the shorthand command:

shell
uv run fastapi dev

The CLI reads the entrypoint from your configuration file and starts the server automatically. For the rest of the course, you can use this shorthand command.

After this edit, your pyproject.toml should look like this:

TOML
[project]
name = "release-tracker"
version = "0.1.0"
description = "A release tracking application to track Projects and Tasks"
readme = "README.md"
authors = [
    { name = "Your Name", email = "you@example.com" }
]
requires-python = "==3.14.*"
dependencies = [
    "fastapi[standard]>=0.115.0",
]

[build-system]
requires = ["uv_build>=0.11.8,<0.12.0"]
build-backend = "uv_build"

[tool.fastapi]
entrypoint = "release_tracker.main:app"