Updated Jun 30, 2026

Setup and Hello, API

This phase is the boring-but-essential part: a clean project folder, an isolated Python environment, the two packages we need, and a single endpoint you can hit in your browser. Get this running and the rest of the weekend is downhill.

You're building this on your own machine, so open a real terminal. I'll show the commands for macOS/Linux and for Windows where they differ.

Make a project folder

Pick a spot you'll remember and create the folder:

mkdir notes-api
cd notes-api

Create a virtualenv

A virtualenv is a private copy of Python for this one project. It keeps the packages you install here from colliding with anything else on your machine. You make it once per project.

# macOS / Linux
python3 -m venv .venv
source .venv/bin/activate
# Windows (PowerShell)
python -m venv .venv
.\.venv\Scripts\Activate.ps1

After activating, your prompt should show (.venv) at the front. That's how you know commands like pip and python are pointing at the project's environment and not the system one. If you open a new terminal later, run the activate command again - the virtualenv doesn't follow you between windows.

If PowerShell refuses to run the activate script with a "running scripts is disabled" error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once and try again.

Install FastAPI and Uvicorn

Two packages. FastAPI is the framework; Uvicorn is the server that runs it.

pip install fastapi uvicorn

That pulls in Pydantic too (FastAPI depends on it), which we'll use heavily in the next phase. Confirm it landed:

pip show fastapi

You should see a version and a location inside your .venv. Good.

Write your first endpoint

Create a file called main.py in the project folder:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"message": "Hello, API"}

Three things are happening here, and they're worth slowing down for because every endpoint you write follows this pattern:

  • app = FastAPI() creates the application object. Everything attaches to it.
  • @app.get("/") is a decorator. It tells FastAPI: when an HTTP GET request arrives for the path /, call the function right below me.
  • The function returns a plain Python dict. FastAPI turns that into a JSON response automatically - you don't serialize anything by hand.

Run the server

Start Uvicorn and point it at your app:

uvicorn main:app --reload

Read main:app as "the object named app, inside the file main.py". The --reload flag tells Uvicorn to restart whenever you save a change - leave it on while you're developing so you're not bouncing the server by hand all weekend.

You'll see output like:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Application startup complete.

Open http://127.0.0.1:8000/ in your browser. You should see:

{"message":"Hello, API"}

You served a real HTTP response from your own machine.

The part that sells FastAPI

Now visit http://127.0.0.1:8000/docs.

That page isn't something you wrote. FastAPI read your code and generated interactive API documentation for you - every endpoint, the methods, the shapes of the data. Right now there's only the one root endpoint, but as you add routes this page fills in automatically. You can click an endpoint, hit "Try it out", and fire a real request without touching curl.

There's a second one at http://127.0.0.1:8000/redoc - same information, a different layout. Pick whichever you like. I lean on /docs constantly while building because the "Try it out" button is faster than typing curl commands.

A mental model of the request flow

Here's what happens on every request from now on:

graph LR
  A[Browser / curl] -->|HTTP GET /| B[Uvicorn]
  B --> C[FastAPI routing]
  C --> D[Your function]
  D -->|return dict| C
  C -->|JSON response| A

Uvicorn accepts the raw connection, FastAPI figures out which of your functions matches the method and path, your function runs and returns a dict, and FastAPI hands the JSON back. You'll add more functions, but the pipe stays the same.

Where we are

You have a project folder, an isolated environment, a running server, one working endpoint, and free interactive docs. Leave the server running with --reload - in the next phase we'll add real routes that take input, and you'll watch the docs update as you save. That's the build step done.