Create Your First FastAPI Endpoint
Create your first FastAPI endpoint with this practical tutorial. Step-by-step instructions, common pitfalls, and what to learn next in the FastAPI Backend Development track.
Focus: create your first FastAPI endpoint
Staring at a blank main.py and wondering where to even start? You know Python, you’ve heard FastAPI is fast and modern, but when it comes to actually creating your first endpoint, the docs feel overwhelming and every tutorial assumes you know more than you do. This lesson cuts through the noise: you’ll go from zero to a running REST API with a GET /health endpoint in under ten minutes, and you’ll understand every line you write. No magic, no fluff — just the fastest path to your first FastAPI endpoint.
The problem this lesson solves
When you’re new to backend development, the biggest hurdle isn’t the language — it’s the sheer number of moving parts: HTTP methods, routing, request/response cycles, JSON, and server configuration. Traditional frameworks like Flask or Django bury you under boilerplate and manual serialization before you’ve even returned your first response. You might spend an hour setting up a project only to realize you’ve built a skeleton, not an API.
FastAPI changes that. But even with its simplicity, beginners still get stuck on basic questions:
- How do I install it and run a server?
- Where does the code live?
- How do I create an endpoint that actually returns something useful?
- Why does my browser show a JSON error instead of my data?
This lesson answers those questions with a single, focused goal: create your first FastAPI endpoint. By the end, you’ll have a working API that returns structured JSON, and you’ll know exactly how it works under the hood.
Who this is for: You’ve completed the earlier lessons in this track (or you’re comfortable with Python syntax and terminal basics). If you’ve ever typed
pip install flaskand felt lost, you’re in the right place.
Core concept / mental model
Think of a web API as a restaurant. The customer (your browser or another service) walks in with an order — that’s the HTTP request. The waiter (FastAPI’s routing engine) reads the order, tells the kitchen (your Python function) what to cook, and then brings back the finished dish (the HTTP response) — usually a plate of JSON.
Every endpoint is just a function that FastAPI decorates with a route. The decorator @app.get("/health") tells FastAPI: “Hey, when someone sends a GET request to /health, run this function and return whatever it gives you.” The function itself is plain Python — no magic, just logic.
Here’s the mental model in one sentence:
FastAPI maps HTTP requests to Python functions, automatically converts inputs to Python objects, and converts return values to JSON.
That’s it. Everything else — path parameters, query strings, request bodies — is just a variation of that core idea.
To make this concrete, let’s break down the anatomy of a FastAPI app:
- FastAPI instance: the
appobject that holds all your routes and configuration. - Path decorator:
@app.get(),@app.post(), etc. — tells FastAPI which HTTP method and path to listen on. - View function: the Python function that executes when the path matches.
- Return value: any Python dict, list, or Pydantic model — FastAPI serializes it to JSON automatically.
How it works step by step
Now let’s trace the journey of a single request through your first endpoint.
-
Install FastAPI and Uvicorn: FastAPI is the framework; Uvicorn is the ASGI server that runs your app. You need both.
-
Write a minimal app: Create a
main.pyfile with aFastAPI()instance and a function decorated with@app.get(). -
Run the server: Use
uvicorn main:app --reloadin your terminal. The--reloadflag auto-restarts the server on code changes during development. -
Send a request: Open your browser to
http://127.0.0.1:8000/healthor usecurl. The browser sends aGETrequest. -
FastAPI matches the path: The routing engine compares
/healthagainst all registered routes. On match, it calls your function. -
Your function runs: Plain Python logic executes — maybe it checks a database or just returns a static dict.
-
FastAPI serializes the response: The dict
{"status": "ok"}becomes a JSON object with the correctContent-Type: application/jsonheader. -
The response travels back: The browser displays the JSON. You just created your first endpoint!
Here’s the same flow in a numbered sequence:
- Request:
GET /health - FastAPI matches route →
read_health() - Function returns
{"status": "ok"} - FastAPI converts to JSON → response
Pro tip: FastAPI automatically generates interactive API documentation at
/docs. After you run the server, open that URL — you can test every endpoint right in the browser, nocurlneeded.
Hands-on walkthrough
Let’s build it together. This is the hands-on exercise you’ve been waiting for: create your first FastAPI endpoint.
Step 1: Set up your environment
Create a new virtual environment (so you don’t pollute your global Python install) and install FastAPI and Uvicorn.
mkdir my-first-api
cd my-first-api
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install fastapi uvicorn
Step 2: Write your first endpoint
Create a file named main.py with the following code:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def read_health():
return {"status": "ok"}
What did we just do?
- Imported
FastAPIand created an instance calledapp. - Decorated
read_healthwith@app.get("/health"). - Returned a plain Python dict — FastAPI handles the JSON serialization.
Step 3: Run the server
uvicorn main:app --reload
You’ll see output like:
INFO: Uvicorn running on http://127.0.0.1:8000
INFO: Application startup complete.
Open your browser to http://127.0.0.1:8000/health. You should see:
{"status":"ok"}
Congratulations — you’ve created your first FastAPI endpoint!
Step 4: Add another endpoint to solidify the pattern
Let’s add a second endpoint that shows how you can handle dynamic data. This time we’ll return a list of items.
from fastapi import FastAPI
app = FastAPI()
items = [
{"name": "laptop", "price": 999},
{"name": "mouse", "price": 25},
]
@app.get("/items")
def list_items():
return {"items": items}
Restart the server (or let --reload do it) and visit http://127.0.0.1:8000/items. You’ll get:
{"items": [{"name": "laptop", "price": 999}, {"name": "mouse", "price": 25}]}
Notice how FastAPI automatically serializes the list of dicts. No json.dumps(), no manual response building — just return Python structures.
Pro tip: Always use
--reloadin development. It saves you from manually restarting the server after every change. In production, you’ll run without it.
Compare options / when to choose what
Now that you have a working endpoint, you might wonder: why FastAPI instead of Flask or Django? Let’s compare the key differences.
| Framework | Boilerplate | Performance | Validation | Async support | Best for |
|---|---|---|---|---|---|
| FastAPI | Minimal | High (asynchronous) | Built-in via Pydantic | Native | Modern APIs, microservices |
| Flask | Low | Moderate | Manual | Manual (third-party) | Small prototypes, simple apps |
| Django | High | Moderate | Manual/forms | Limited | Full-stack apps with ORM, admin |
FastAPI shines when you need:
- Fast development with automatic validation and docs.
- High performance for I/O-bound operations (async endpoints).
- A clear, modern syntax that scales from prototype to production.
Flask wins if:
- You already know it, or you’re building a tiny internal tool.
- You need extreme flexibility and don’t mind handling validation manually.
Django wins if:
- You need a full-fledged admin panel, ORM, and authentication built-in.
- You’re building a traditional multi-page web app, not just an API.
Variations: path vs. query parameters
When you create endpoints, you’ll often need to capture values from the URL. Two common approaches:
- Path parameters:
/items/{item_id}— used to identify a resource (e.g.,GET /items/1). - Query parameters:
/search?q=laptop— used for filtering, sorting, or pagination.
FastAPI supports both with clean decorators. Later lessons will dive deep into these, but here’s a teaser:
@app.get("/items/{item_id}")
def get_item(item_id: int):
return {"item_id": item_id}
@app.get("/search")
def search(q: str = ""):
return {"query": q}
Troubleshooting & edge cases
Even with this simple example, you’ll hit common pitfalls. Here are the most frequent ones and how to fix them.
1. “ModuleNotFoundError: No module named ‘fastapi’”
You forgot to activate your virtual environment, or you installed in a different environment. Run pip list | grep fastapi to check. If it’s missing, reinstall inside the activated venv.
2. “Address already in use”
Port 8000 is occupied. Either close the other process or change the port: uvicorn main:app --reload --port 8001.
3. Browser shows “Internal Server Error”
Check the terminal output. A common cause is a Python exception in your function (e.g., NameError). FastAPI prints the full traceback — read it and fix the code.
4. Wrong URL / 404
Make sure the path matches exactly, including trailing slashes. /health and /health/ are different routes by default. Be consistent.
5. Returning non-JSON data
If you return a plain string like "hello", FastAPI will return it as text, not JSON. To force JSON, return a dict or list. If you need custom serialization, you’ll use JSONResponse later.
6. CORS errors
If you’re building a frontend that calls your API from a different origin (e.g., localhost:3000 to localhost:8000), you’ll need to configure CORS middleware. We’ll cover that in a later lesson.
Pro tip: If your endpoint returns unexpected JSON (like
null), check that you’re returning a value. A function that ends without areturnimplicitly returnsNone.
What you learned & what's next
You’ve just taken the first step toward building production-ready APIs. Let’s recap what you accomplished:
- You understand the core idea behind create your first FastAPI endpoint: FastAPI maps HTTP requests to Python functions and automates JSON conversion.
- You completed a practical exercise: You built a working
GET /healthendpoint and added a second endpoint returning a list of items. - You connected the concept to the next lesson: Every future lesson builds on this foundation — routing, request validation, dependency injection, and beyond.
You can now:
- Set up a FastAPI project with a virtual environment.
- Write a basic endpoint using decorators.
- Run the server with Uvicorn and test with your browser or
curl. - Spot and fix common errors when your first endpoint doesn’t work.
What’s next: In the next lesson, you’ll dive into path parameters and query parameters — how to build endpoints that accept dynamic input from the client. You’ll learn how to validate data types, define defaults, and handle optional parameters. That’s where FastAPI starts to feel truly magic.
Keep this simple app as your sandbox. Play with adding more endpoints, changing return values, and observing the auto-generated docs at /docs. The more you experiment, the faster the mental model sticks.
You’ve created your first FastAPI endpoint — welcome to the world of modern API development!
Practice recap
Mini challenge: Extend your main.py with a /status endpoint that returns a dict with a message key and a random number. Run the server, hit the endpoint, and view the auto-generated docs at /docs to see your new route. This will solidify the process from writing code to seeing live output.
Common mistakes
- Forgetting to activate the virtual environment before installing FastAPI, leading to
ModuleNotFoundError. - Using a different port than 8000 and then trying to access the default URL, getting a 404.
- Returning a plain string and expecting JSON — FastAPI returns it as text unless you return a dict, list, or Pydantic model.
- Mismatching the URL path (including trailing slashes) —
/healthand/health/are different routes. - Leaving out a
returnstatement, which makes FastAPI return anullbody (or empty response).
Variations
- Use
@app.post()instead of@app.get()to handle client-supplied data (e.g., creating a resource) — the same mental model applies. - Install
fastapi[all](includesuvicorn[standard]and other optional dependencies) to get extra features like enum support and graceful shutdown. - Try the
--portflag onuvicornto run the server on a non-default port, useful for testing or when 8000 is busy.
Real-world use cases
- A microservice that exposes a
/healthendpoint for Kubernetes liveness and readiness probes. - A simple REST API for a mobile app returning product listings via a
GET /itemsendpoint. - A backend service that aggregates data from multiple sources and exposes it via a single
GET /summaryendpoint.
Key takeaways
- A FastAPI endpoint is just a Python function decorated with a route like
@app.get("/health"). - FastAPI automatically converts Python dicts/lists to JSON responses, eliminating manual serialization.
- Run your app with
uvicorn main:app --reloadfor instant feedback during development. - The interactive docs at
/docslet you test endpoints without external tools. - Common errors are usually environmental (virtual env, port) or path mismatches — check the terminal traceback.
- This pattern of defining routes will be reused in every subsequent lesson, so master it now.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.