FastAPI from Scratch: Installation, app = FastAPI(), a First Route and Running the Server
- Create and activate a virtual environment (venv) and install FastAPI in it
- Write main.py with
app = FastAPI()and a first Route, and understand every line - Run the server with uvicorn, and understand every part of the command and of the output
- Test the server in the browser, with curl and in the /docs page
What will we learn?
This lesson is a full setup, from scratch, of a real server:
- A virtual environment (venv): where libraries are installed.
- Installing FastAPI with
pip. main.py: importing, creatingapp, and a first Route.- Running it with
uvicorn. - Testing: the browser, curl, and the automatic documentation page
/docs.
Why do we need this?
In the networking topic we saw what a “manual” server involves: bind, listen, accept, and decoding text. That is a lot of work, and it is easy to crash on bad input. FastAPI does all of this for us: it receives the request, decodes HTTP, finds the right function, converts the response to JSON, and checks types. We write only the logic.
FastAPI is one of the most common libraries for a Backend in Python, especially for APIs and in the worlds of Data and AI.
Explanation from the basics
What is FastAPI?
FastAPI is a Framework for building Web APIs in Python. A Framework = a library that dictates a structure: “you write functions, and I call them when a matching request arrives”.
Uvicorn is the HTTP server itself: the program that listens on a port (the networking topic: listen), receives connections, and passes every request to FastAPI. FastAPI = what to answer. Uvicorn = the reception at the entrance.
Browser ──HTTP──► Uvicorn (listens on port 8000) ──► FastAPI (finds the right function) ──► your function
◄──JSON── Uvicorn ◄── FastAPI (converts dict to JSON) ◄──────────────────────── return {...}An external library and pip
Until now we used only modules that come with Python (os, json, logging). FastAPI is an external library (a Third-party Package), so it has to be installed.
pip is Python’s package manager: it downloads libraries from PyPI (Python’s public library repository) and installs them.
A virtual environment (venv) — and why it is needed
If you install libraries “globally” (for the whole computer), one project needs FastAPI version 0.100, and another project needs 0.142, and there is a conflict. A Virtual Environment (venv) = a private folder for the project, with its own copy of Python and of its libraries. Every project with its own libraries, without conflicts.
A handyman who works on three houses does not keep all the tools in one pile at home. Every work site has a toolbox of its own, with exactly what the site needs. venv = the project’s toolbox. And “activating” it (activate) = “I am working now with this box”.
Decorator — @app.get("/")
A line that starts with @ above a def is a Decorator (we saw @property in the OOP topic). In FastAPI, @app.get("/") says: “register the function below as the handler of GET requests to the path /”.
Remember the calculator with a dict of functions (OPERATIONS = {"+": add, ...})? FastAPI builds exactly such a table: (Method, Path) → function. Every @app.get(...) adds a row to the table. When a request arrives, FastAPI looks it up in the table and calls the function. (This is Functions as Objects in action!)
Route = the link between an Endpoint (Method + Path) and the function that handles it.
Syntax
Step 1 — a folder and a virtual environment
mkdir ~\dev\kodkod6\week-4\hello-api
cd ~\dev\kodkod6\week-4\hello-api
python -m venv .venvStep 2 — activating the environment
Windows (PowerShell):
.venv\Scripts\Activate.ps1Mac / Linux:
source .venv/bin/activateThe Prompt changes and starts with (.venv). From now on python and pip belong to the project.
Step 3 — installing
pip install "fastapi[standard]"[standard] installs FastAPI together with Uvicorn and a few more useful tools. A check:
pip listStep 4 — main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def home():
return {"message": "Hello from my first server!"}Step 5 — running
uvicorn main:app --reloadStep 6 — testing
- The browser:
http://127.0.0.1:8000/ - The documentation:
http://127.0.0.1:8000/docs - Stopping:
Ctrl + C
First code example
After steps 1–5, the Terminal shows:
INFO: Will watch for changes in these directories: ['C:\\Users\\dana\\dev\\kodkod6\\week-4\\hello-api']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [43864] using StatReload
INFO: Started server process [29224]
INFO: Waiting for application startup.
INFO: Application startup complete.In the browser, http://127.0.0.1:8000/:
{"message":"Hello from my first server!"}And in the Terminal a line about the request is added:
INFO: 127.0.0.1:52311 - "GET / HTTP/1.1" 200 OKBreaking down the code line by line
main.py:
| Line | Explanation |
|---|---|
from fastapi import FastAPI | Imports the Class named FastAPI from the library (the modules lesson: from ... import). |
app = FastAPI() | Creates an object from the Class (the OOP topic!). app is the application: it holds the table of Routes, and Uvicorn will talk to it. |
@app.get("/") | A Decorator: “register home as the handler of GET /”. get is a method of the object app. |
def home(): | A regular function. The name is free. FastAPI recognizes it by the Decorator, not by its name. |
return {"message": "..."} | You return a dict, and FastAPI converts it to JSON automatically (Serialization, the JSON lesson) and returns Status 200. |
The command uvicorn main:app --reload:
| Part | Explanation |
|---|---|
uvicorn | Starting the HTTP server. |
main | The module: the file main.py (without .py). |
:app | The object inside the module: the variable app. “Run the application called app, which is in main”. |
--reload | Development mode: when you save a change in the code, the server restarts itself automatically. Only for development, not on a real server. |
The output lines:
| Line | Meaning (and the connection to the networking topic) |
|---|---|
Will watch for changes... | The --reload mode: watching the files in the folder. |
Uvicorn running on http://127.0.0.1:8000 | The server listens (listen) on localhost, port 8000. |
Started reloader process [43864] | A “watcher” process that detects changes. A PID. |
Started server process [29224] | The process that really handles the requests. Another PID. |
"GET / HTTP/1.1" 200 OK | A log of every request: the Method, the Path, the version and the Status Code. |
Another example
More Routes, and /docs. Update main.py (with --reload, the server updates by itself the moment you save):
from fastapi import FastAPI
app = FastAPI(title="Kodkod Hello API")
COURSE = {"name": "Kodkod 6", "weeks": 5, "topics": ["OS", "Python", "OOP", "Backend", "DB"]}
@app.get("/")
def home():
return {"message": "Hello from my first server!"}
@app.get("/course")
def course_info():
return COURSE
@app.get("/course/topics")
def course_topics():
return COURSE["topics"]
@app.get("/health")
def health():
return {"status": "ok"}Tests with curl (in another Terminal, because the first one is busy with the server):
curl.exe http://127.0.0.1:8000/course
curl.exe http://127.0.0.1:8000/course/topics
curl.exe -i http://127.0.0.1:8000/nothingOutput:
{"name":"Kodkod 6","weeks":5,"topics":["OS","Python","OOP","Backend","DB"]}
["OS","Python","OOP","Backend","DB"]
HTTP/1.1 404 Not Found
date: Tue, 06 Oct 2026 14:30:00 GMT
server: uvicorn
content-length: 22
content-type: application/json
{"detail":"Not Found"}Breakdown:
FastAPI(title="Kodkod Hello API")— a name for the application, which will appear in the documentation page.- You can return anything JSON knows how to represent: a dict (
/course), a list (/course/topics), a string or a number. - A path that was not defined (
/nothing): FastAPI returns a 404 by itself, with an error JSON. We did not write a single line for it. /health— an Endpoint common on real servers: monitoring systems check it every few seconds, to know that the server is alive.
/docs — the automatic documentation
Open http://127.0.0.1:8000/docs. FastAPI built by itself an interactive documentation page (called Swagger UI):
- A list of all the Endpoints, with the Method and the Path.
- Clicking an Endpoint → Try it out → Execute: sends a real request and shows the response.
- The documentation updates automatically with every new Route.
In the students API topic we will use it as the server’s main testing tool.
Common mistake
Mistake 1: installing without activating the venv, or opening a new Terminal and forgetting to activate it.
uvicorn : The term 'uvicorn' is not recognized ...Or:
ModuleNotFoundError: No module named 'fastapi'A check: does the Prompt start with (.venv)? If not, .venv\Scripts\Activate.ps1. In every new Terminal, activate it again. (An alternative that always works: python -m uvicorn main:app --reload.)
Mistake 2: on Windows, activation is blocked.
.venv\Scripts\Activate.ps1 cannot be loaded because running scripts is disabled on this system.PowerShell blocks running scripts by default. Open PowerShell and run once:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser(This allows local scripts for your user only. Remember permissions from the operating system topic!)
Mistake 3: main:app does not match the code.
ERROR: Error loading ASGI app. Attribute "app" not found in module "main".The file is not named main.py, or the variable is not named app (for example application = FastAPI()). The format is always <file>:<variable>. And you run it from the folder where the file is (the Current Working Directory, remember the operating system topic!).
Mistake 4: a port that is taken.
ERROR: [WinError 10048] error while attempting to bind on address ('127.0.0.1', 8000): only one usage of each socket address (protocol/network address/port) is normally permittedAnother server (or a previous copy of the same server) is already listening on 8000 (remember ports from the networking topic!). Stop it with Ctrl + C in its Terminal, or run on another port: uvicorn main:app --reload --port 8001.
Mistake 5: putting .venv into Git. The folder is huge and contains thousands of files, which depend on your computer. Add to .gitignore:
.venv/
__pycache__/And instead, save a list of the libraries: pip freeze > requirements.txt. Whoever downloads the project runs pip install -r requirements.txt.
Practice
The exercises use only what was taught in this lesson and the lessons before it. Try on your own first, and only then open the solution.
Exercise 1: Setting up a project
Write the sequence of commands that sets up a project hello-api: a folder, a virtual environment, activating it (Windows), and installing FastAPI. What is the role of the virtual environment?
Show solution
mkdir hello-api
cd hello-api
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install "fastapi[standard]"
pip list(On Mac: source .venv/bin/activate.)
A virtual environment (venv) isolates the project’s packages: every project with its own versions, without conflicting with other projects or with the computer’s Python. The .venv folder does not go into Git.
Exercise 2: A first Route
Write a main.py with a FastAPI application and a Route for / that returns {"message": "Hello from my first server!"}. How do you run it, and which address do you browse to?
Show solution
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def home():
return {"message": "Hello from my first server!"}Run it:
uvicorn main:app --reloadAnd browse to http://127.0.0.1:8000. main:app = the file main.py, the variable app. --reload restarts the server after every save.
Exercise 3: From a request to a function
Put in order the path of a request, from the browser to our function and back:
- (a) FastAPI converts the dict to JSON
- (b) The browser sends
GET / - (c) The function
home()returns a dict - (d) Uvicorn receives the request on port 8000
- (e) FastAPI finds the function that matches
GET /
Show solution
The order: (b), (d), (e), (c), (a)
- The browser sends
GET /. - Uvicorn — the server itself — receives the request on port 8000.
- FastAPI finds the Route that matches the Method and the path.
- Our function returns a regular dict.
- FastAPI converts it to JSON and returns a response with 200.
Exercise 4: Quotes API
Write a quotes API with a list of quotes (dictionaries with text and author), and two Routes:
GET /quotes— all the quotes;GET /quotes/count—{"count": <number>}.
Give the application the title "Quotes API".
Show solution
from fastapi import FastAPI
app = FastAPI(title="Quotes API")
QUOTES = [
{"text": "Simple is better than complex.", "author": "The Zen of Python"},
{"text": "Readability counts.", "author": "The Zen of Python"},
{"text": "Talk is cheap. Show me the code.", "author": "Linus Torvalds"},
]
@app.get("/quotes")
def all_quotes():
return QUOTES
@app.get("/quotes/count")
def count_quotes():
return {"count": len(QUOTES)}GET /quotes/count returns {"count": 3}. FastAPI converts a list to JSON too. The title appears in the documentation page at /docs.
Exercise 5: Swagger UI
- What is at the address
http://127.0.0.1:8000/docs? - How do you use it to test a Route?
Show solution
- Swagger UI — a documentation page that FastAPI builds automatically from the code: all the Routes, the Method of each one, the parameters and the responses.
- Open a Route, click Try it out, fill in parameters if there are any, and click Execute. You see the request that was sent, the Status Code and the response body — without writing a Client.
Summary
- FastAPI = a Framework in which you define Routes. Uvicorn = the HTTP server that listens on the port and passes requests to FastAPI.
- venv:
python -m venv .venv, activating it (Activate.ps1/source .venv/bin/activate), and thenpip install "fastapi[standard]". In every new Terminal, activate it again. app = FastAPI()= the application object.@app.get("/path")registers a function as the handler ofGET /path.- Return a dict or a list, and FastAPI converts it to JSON with 200. A path that does not exist gets a 404 automatically.
- Running:
uvicorn main:app --reload(file:variable), from the file’s folder. Stopping:Ctrl + C. /docs= interactive, automatic documentation and testing..venv/in.gitignore.requirements.txtin Git.