Setup and Your First App
Install FastAPI in a virtual environment, write a five-line app, run it with fastapi dev, and open the automatic docs. Then see auto-reload work - and the four errors almost every beginner meets on day one.
What you will be able to do
- Create a virtual environment and install FastAPI with its standard extras
- Write and run your first FastAPI app
- Use the three free pages: /docs, /redoc and /openapi.json
- Explain fastapi dev, fastapi run and uvicorn, and when to use each
- Use auto-reload while you work
- Fix the common first-day errors
The idea, in plain English
Before you write an API you need three things: Python, a separate place to install packages (a virtual environment), and FastAPI itself. Then a FastAPI app is one Python file - five lines are enough for a working API.
You run the app with the fastapi command. fastapi dev starts a server on your own computer and restarts it every time you save the file, so you see your changes at once. Your API then answers at http://127.0.0.1:8000 - and FastAPI also gives you an interactive documentation page where you can try every endpoint in the browser.
In this lesson we set everything up, run the first app, look at the free pages, watch auto-reload, and then make the common beginner mistakes on purpose, so you recognise them when they happen. FastAPI 0.143.0, FastAPI CLI 0.0.32, Python 3.11.
Worked example: A "Hello, FastAPI!" app: run it, open /docs, add a route while it runs, and break it on purpose four ways.
1 and 2 - a clean place, then FastAPI
A virtual environment keeps this project’s packages separate. fastapi[standard] installs FastAPI plus the server, the CLI and httpx.
The steps of this lesson, with what each one printed on our machine.
Words you will see in this lesson
A few setup words.
Virtual environmentA folder (.venv) with its own Python packages, separate for each project.pipPython’s tool for installing packages.ServerA program that waits for requests on a port and answers them. Uvicorn, here.PortA number that says which program on a computer gets a request: 8000, 8111...127.0.0.1This computer itself, also called localhost. Only you can reach it.Auto-reloadThe server restarts by itself when you save a file.OpenAPIA standard way to describe an API in JSON. FastAPI writes it for you.An everyday example: opening a small shop
Opening a shop takes a few steps. You rent a space of your own (the virtual environment - your things do not mix with the neighbours’). You bring the equipment (pip install). You set up the counter with one product (main.py). You open the door and turn on the sign (fastapi dev). And the shop comes with a printed catalogue that updates itself whenever you add a product (/docs).
Step 1 - a virtual environment and FastAPI
Make a folder for the project and create a virtual environment inside it. Activate it, so pip installs into it and python uses it. Then install FastAPI with the standard extras.
Why "fastapi[standard]" and not just fastapi? The [standard] part adds the pieces you need on day one: Uvicorn (the server), the fastapi command, and httpx (used by tests and by our Python client in Lesson 1.1). Remember the quotes - some terminals treat [ and ] as special characters.
$ mkdir todo-api && cd todo-api
$ python -m venv .venv # create the environment
$ source .venv/bin/activate # use it in this terminal
$ pip install "fastapi[standard]" # FastAPI + server + CLI + httpx
$ python -c "import importlib.metadata as m; print(m.version('fastapi'))"
0.143.0Step 2 - your first app
Create main.py with these lines. FastAPI() makes the app object - the whole API lives on it. @app.get("/") is a decorator: it says "when a GET request comes to the path /, call the function below". The function returns a dict, and FastAPI sends it as JSON.
The function name (home) is up to you - it is used in the docs, not in the URL. The URL comes only from the decorator.
from fastapi import FastAPI # the FastAPI class
app = FastAPI() # the app: every route is added to this object
@app.get("/") # GET requests to "/" call the function below
def home():
return {"message": "Hello, FastAPI!"} # a dict -> JSONStep 3 - run it with fastapi dev
Run fastapi dev main.py. It finds the app object in the file on its own - the output says "Using import string: main:app", which means "the object called app in the module main". It starts the Uvicorn server, prints the address and the docs address, and starts a reloader that watches your folder.
Leave this terminal running. Open http://127.0.0.1:8000 in your browser, or use curl in a second terminal. (We used --port 8100 in our test; without --port it is 8000.) Stop the server with Ctrl+C.
⚡️ Starting FastAPI in development mode
🐍 Using import string: main:app
🌐 Server started at http://127.0.0.1:8100
Documentation at http://127.0.0.1:8100/docs
Logs:
INFO: Will watch for changes in these directories: ['/.../todo-api']
INFO: Uvicorn running on http://127.0.0.1:8100 (Press CTRL+C to quit)
INFO: Started reloader process [9774] using WatchFiles
INFO: Started server process [9793]
INFO: Waiting for application startup.
INFO: Application startup complete.$ curl localhost:8100/
{"message":"Hello, FastAPI!"}
$ curl -i localhost:8100/
HTTP/1.1 200 OK
date: Sat, 10 Oct 2026 20:12:07 GMT
server: uvicorn
content-length: 29
content-type: application/json
$ curl localhost:8100/nothing
{"detail":"Not Found"} # status 404Step 4 - three pages you get for free
Open http://127.0.0.1:8000/docs. This is Swagger UI: a list of every endpoint. Click one, press "Try it out", then "Execute", and the browser sends a real request to your API and shows the response. You will use this page all through the course.
/redoc shows the same information in a different, read-only layout. /openapi.json is the source of both: a JSON description of your API in the OpenAPI 3.1.0 standard. Other tools can read it - for example to generate client code in another language. You did not write any of it; FastAPI built it from your code.
$ curl -s localhost:8100/docs | grep -o "<title>[^<]*</title>"
<title>FastAPI - Swagger UI</title>
$ curl -s localhost:8100/redoc | grep -o "<title>[^<]*</title>"
<title>FastAPI - ReDoc</title>
$ curl -s localhost:8100/openapi.json
{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/":{"get":{"summary":"Home","operationId":"home__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}}}Tip: Look at "summary":"Home" in openapi.json: FastAPI made it from the function name home. Good function names make good docs.
Step 5 - auto-reload: change code while it runs
With the server still running, we added a second route to main.py and saved the file. The log printed "WatchFiles detected changes in 'main.py'. Reloading...", stopped the old server process and started a new one. A second later the new route answered.
This is why fastapi dev is for development: you never restart by hand. The {name} part in the new path is a path parameter - Lesson 1.4 explains it.
@app.get("/hello/{name}")
def hello(name: str):
return {"message": f"Hello, {name}!"}WARNING: WatchFiles detected changes in 'main.py'. Reloading...
INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [9793]
INFO: Started server process [9878]
INFO: Waiting for application startup.
INFO: Application startup complete.
$ curl localhost:8100/hello/Ravi
{"message":"Hello, Ravi!"}fastapi dev, fastapi run, uvicorn - which one?
fastapi dev is for writing code: auto-reload on, and it listens on 127.0.0.1 only, so no other computer can reach it. fastapi run is for production: no reload, and it listens on 0.0.0.0 - every network address of the machine. We ran both: "Starting FastAPI in development mode" at 127.0.0.1, and "Starting FastAPI in production mode" at 0.0.0.0.
Both commands use Uvicorn underneath. Many tutorials call Uvicorn directly - uvicorn main:app --reload - which does the same as fastapi dev. Here you must write the import string yourself: main:app means "the object app in main.py".
⚡️ Starting FastAPI in production mode
🐍 Using import string: main:app
🌐 Server started at http://0.0.0.0:8102
Documentation at http://0.0.0.0:8102/docs
INFO: Started server process [9998]
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8102 (Press CTRL+C to quit)fastapi dev main.pyDevelopment: auto-reload, 127.0.0.1 (this computer only).fastapi run main.pyProduction: no reload, 0.0.0.0 (reachable on the network). Module 10.uvicorn main:app --reloadThe same server, called directly. You name the module and the app object.Four first-day errors - tried on purpose
Running python main.py does nothing. The file only creates the app; nothing starts a server. It ran, exited with code 0, and printed nothing. Use fastapi dev main.py.
Starting a second server on a port that is already in use fails with "[Errno 48] Address already in use" (the number can differ on Linux and Windows). Another server - maybe one you forgot in another terminal - already has that port. Stop it, or use --port 8001.
A wrong file name fails with "Path does not exist app.py". Check the name and the folder you are in.
One surprise that is NOT an error: we named the app object api instead of app, and fastapi dev still found it ("Using import string: other:api"). The fastapi command looks for a FastAPI object in the file. uvicorn does not: there you must write other:api yourself.
$ python main.py
$ echo $?
0 # it ran, did nothing, and exited
$ fastapi dev main.py --port 8100 # while another server uses 8100
ERROR: [Errno 48] Address already in use
$ fastapi dev app.py
Path does not exist app.py
$ fastapi dev other.py --port 8101 # app object named "api"
🐍 Using import string: other:api
🌐 Server started at http://127.0.0.1:8101A tidy project folder
For now, one main.py is enough. Keep the virtual environment inside the project, and tell git to ignore it - it can be rebuilt any time from a list of packages. Module 8 splits a bigger app into several files.
todo-api/
├── .venv/ # the virtual environment (do not commit it)
├── main.py # your app
├── requirements.txt # pip freeze > requirements.txt
└── .gitignore # contains: .venv/Setup at a glance
Create a venvOne per project.
python -m venv .venv
Activate itmacOS/Linux; Windows: .venv\Scripts\activate
source .venv/bin/activate
InstallWith server, CLI and httpx.
pip install "fastapi[standard]"
Run while codingAuto-reload, this computer only.
fastapi dev main.py
Run in productionNo reload, all addresses.
fastapi run main.py
Another portIf 8000 is busy.
fastapi dev main.py --port 8001
Free pagesDocs, docs, and the description.
/docs /redoc /openapi.json
Try it yourself
The code does not change. Swap the content string and the program does something else entirely.
“Open /docs, expand GET /hello/{name}, press "Try it out", type your name and press "Execute". Find the request URL Swagger used.”
“Change the message in home() and save. Find the "Reloading..." line in the terminal, then refresh the browser.”
“Make a typo in main.py (remove a bracket) and save. What does the server log show? Fix it and watch it recover.”
“After adding /hello/{name}, open /openapi.json. Find the new path and its parameter.”
What usually goes wrong
python main.py only creates the app object and exits. Start a server with the fastapi command.
✗ python main.py✓ fastapi dev main.pyIf the venv is not active, pip installs into another Python and fastapi may not be found later. Activate first; check with which python.
Some shells (like zsh) treat [ ] as special. Quote the package name.
✗ pip install fastapi[standard]✓ pip install "fastapi[standard]""Address already in use" means another server already listens there. Stop it with Ctrl+C in its terminal, or pick another port.
Reload restarts on every file change and it listens only on 127.0.0.1. For real users use fastapi run (Module 10).
Key points
- Use one virtual environment per project and install "fastapi[standard]" into it.
- An app is app = FastAPI() plus decorated functions; a returned dict becomes JSON.
- fastapi dev runs it with auto-reload on 127.0.0.1; fastapi run is for production on 0.0.0.0.
- /docs, /redoc and /openapi.json are built from your code automatically.
- python main.py does nothing - it starts no server.
- "Address already in use" means the port is taken: stop the other server or change --port.
Quick check before you move on
Interview questions
How do you run a FastAPI app in development and in production?
In development, fastapi dev main.py (or uvicorn main:app --reload) for auto-reload on localhost. In production, fastapi run (Uvicorn without reload, bound to 0.0.0.0), usually with several workers, behind a proxy, often in a container.
What is OpenAPI and how does FastAPI use it?
A standard JSON/YAML format for describing HTTP APIs. FastAPI generates the OpenAPI schema from routes, type hints and models, and serves it at /openapi.json; Swagger UI (/docs) and ReDoc (/redoc) render it.
Why use a virtual environment?
To keep each project’s package versions separate and reproducible, so installing or upgrading one project does not break another.
Quiz
- 1.
What is the difference between fastapi dev and fastapi run?
- 2.
You named your app object api. Will fastapi dev find it? Will uvicorn main:app?
- 3.
What does /openapi.json contain?
- 4.
What happened in the log when we saved main.py?
Comments
Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.
Loading comments...