← Back to FastAPI
Lesson 1.2 · Getting Started

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.

Beginner25 min

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.

workflowFrom an empty folder to a running APIstep 1 / 4

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.

fastapi
0.143.0
uvicorn
0.54.0
pydantic
2.14.0
fastapi-cli
0.0.32

The steps of this lesson, with what each one printed on our machine.

Words you will see in this lesson

A few setup words.

Small dictionary
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.

Set up (macOS and Linux; on Windows activate with .venv\Scripts\activate)
$ 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.0

Step 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.

Example 1 - main.py
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 -> JSON

Step 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.

Output - fastapi dev main.py --port 8100
⚡️ 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.
Output - asking the API (second terminal)
$ 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 404

Step 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.

Output - the free pages
$ 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.

Added to main.py while the server runs
@app.get("/hello/{name}") def hello(name: str): return {"message": f"Hello, {name}!"}
Output - the server log, then the new route
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".

Output - fastapi run main.py --port 8102 (shortened)
⚡️ 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)
Three ways to start the server
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.

Output - the four cases
$ 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:8101

A 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.

A simple layout
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 venv

One per project.

python -m venv .venv
Activate it

macOS/Linux; Windows: .venv\Scripts\activate

source .venv/bin/activate
Install

With server, CLI and httpx.

pip install "fastapi[standard]"
Run while coding

Auto-reload, this computer only.

fastapi dev main.py
Run in production

No reload, all addresses.

fastapi run main.py
Another port

If 8000 is busy.

fastapi dev main.py --port 8001
Free pages

Docs, 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.

Try it out

“Open /docs, expand GET /hello/{name}, press "Try it out", type your name and press "Execute". Find the request URL Swagger used.”

Watch reload

“Change the message in home() and save. Find the "Reloading..." line in the terminal, then refresh the browser.”

Break it

“Make a typo in main.py (remove a bracket) and save. What does the server log show? Fix it and watch it recover.”

Read openapi.json

“After adding /hello/{name}, open /openapi.json. Find the new path and its parameter.”

What usually goes wrong

Running the file with python

python main.py only creates the app object and exits. Start a server with the fastapi command.

✗ python main.py
✓ fastapi dev main.py
Installing without the virtual environment

If the venv is not active, pip installs into another Python and fastapi may not be found later. Activate first; check with which python.

Forgetting the quotes

Some shells (like zsh) treat [ ] as special. Quote the package name.

✗ pip install fastapi[standard]
✓ pip install "fastapi[standard]"
Two servers on one port

"Address already in use" means another server already listens there. Stop it with Ctrl+C in its terminal, or pick another port.

Using fastapi dev in production

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

What does fastapi[standard] add to plain fastapi?
The Uvicorn server, the fastapi command, and httpx - what you need to run and test on day one.
What does "Using import string: main:app" mean?
Load the object called app from the module main (main.py).
Where can you try your endpoints in the browser?
At /docs, with "Try it out" and "Execute".
Why did python main.py print nothing?
The file only defines the app; nothing starts a server, so it exits at once.

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. 1.

    What is the difference between fastapi dev and fastapi run?

  2. 2.

    You named your app object api. Will fastapi dev find it? Will uvicorn main:app?

  3. 3.

    What does /openapi.json contain?

  4. 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...