Build a Task Manager REST API
Build a CRUD REST API with FastAPI, Pydantic validation, and Swagger UI.
Introduction
30 Second Summary
Every time you open a mobile app, check a website, or place an order online, a REST API is working behind the scenes to move data between clients and servers. This is the foundational architecture of the modern web.
In this project, you will build a Task Manager REST API from scratch using FastAPI. You will create endpoints that handle creating, reading, updating, and deleting tasks, complete with automatic data validation and interactive documentation.
What You'll Build
A fully functional REST API that manages tasks through all four CRUD operations, with automatic request validation, proper error handling, and live interactive documentation you can test in your browser.
By the end of this project, you'll have:
- A FastAPI application with 5 endpoints covering all CRUD operations (Create, Read, Update, Delete).
- Pydantic data models that automatically validate incoming requests and serialize responses.
- Proper error handling that returns meaningful HTTP status codes and error messages.
- Secret Mission: Add filtering and sorting to your task list endpoint so clients can query by completion status and sort by title.
Are there any prerequisites?
You should be comfortable with basic Python concepts like functions, dictionaries, and type hints. No prior web development or API experience is needed.
Set Up Your Project
Before you can build your REST API, you need a clean workspace with the right tools installed.
In this step, you'll create a project folder, set up a Python virtual environment to keep your dependencies isolated, and install FastAPI. By the end, you'll have a running server you can visit in your browser.
In this step, get ready to:
- Create a project directory and virtual environment.
- Install FastAPI with all standard dependencies.
- Create a minimal app and verify it runs in your browser.
Create a project directory and virtual environment
A virtual environment gives your project its own isolated set of Python packages. This prevents conflicts with other projects on your machine.
- Open your terminal.
How do I open a terminal?
Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar. Type Terminal (macOS) or Command Prompt (Windows) and press Enter.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir task-manager-api
cd task-manager-api
- Confirm the folder exists by running:
ls
You should see an empty output since the folder is brand new. That's expected.
- Create a Python virtual environment by running:
python -m venv venv
What does this command do?
python -m venv venv tells Python to create a virtual environment in a folder called venv. This folder holds a self-contained copy of Python and any packages you install, keeping them separate from your system Python.
- Activate the virtual environment:
macOS
- Run this command to activate:
source venv/bin/activate
Windows
- Run this command to activate:
venv\Scripts\activate
You should see (venv) appear at the start of your terminal prompt. This confirms the virtual environment is active.
Don't see (venv) in your prompt?
If you see an error like "python: command not found", try using python3 instead of python in the venv creation command.
On Windows, if you get a permissions error in PowerShell, run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned first, then try activating again.
Help me activate my Python virtual environment
Install FastAPI
FastAPI is a modern Python web framework for building APIs. The [standard] option bundles everything you need for development, including the dev server and auto-generated documentation.
- Install FastAPI with all standard dependencies by running:
pip install "fastapi[standard]==0.136.3"
What does [standard] include?
The [standard] extra installs the development server (with auto-reload), the FastAPI CLI, and Swagger UI for interactive documentation. Without it, you'd need to install these separately.
- Verify the installation succeeded by running:
fastapi --help
You should see a help message listing available commands including dev and run. This confirms FastAPI and its CLI are installed correctly.
Seeing "command not found" for fastapi?
Make sure your virtual environment is active. You should see (venv) in your terminal prompt. If not, re-run the activation command from the previous substep.
If the install itself failed with a network error, check your internet connection and try running the pip install command again.
Help me troubleshoot my FastAPI installation
Create your project file and verify it runs
Every FastAPI application starts with a single Python file that defines your app and its endpoints. You'll create a minimal version first to confirm everything works.
- Create a new file called main.py in your task-manager-api folder.
- Copy the following code into it:
from fastapi import FastAPI
app = FastAPI(title="Task Manager API")
@app.get("/")
async def root():
return {"message": "Hello World"}
What does this code do?
- FastAPI(title="Task Manager API") creates your application instance. The title shows up in the auto-generated documentation.
- @app.get("/") registers the root() function to handle GET requests at the root URL path.
- The function returns a Python dictionary, which FastAPI automatically converts to JSON.
- Save main.py.
- Start the development server by running:
fastapi dev main.py
You should see output indicating the server is running at http://127.0.0.1:8000. The server auto-reloads when you save changes to your code.
- Open your browser and visit http://127.0.0.1:8000.
You should see a JSON response: {"message":"Hello World"}. This confirms your FastAPI server is running.
- Now visit http://127.0.0.1:8000/docs in your browser.
You should see the Swagger UI interactive documentation page. This is generated automatically from your code. You'll use it throughout this project to test your API endpoints.
Server not starting?
If you see an error about the port being in use, another process is already running on port 8000. Close that process or stop any other dev servers you have running.
If you see a "module not found" error, make sure your virtual environment is active (look for (venv) in your prompt) and that you're running the command from inside the task-manager-api folder.
Help me get my FastAPI dev server running
✔️ Awesome, I've got everything!
Great. Double check you've saved your file, then continue to the next step.
ⓧ I'd like to double check the full code
Here's the complete main.py file you should have at this point:
from fastapi import FastAPI
app = FastAPI(title="Task Manager API")
@app.get("/")
async def root():
return {"message": "Hello World"}
Your development environment is ready and your server is running. Next up, you'll define the data models that describe what a task looks like.
Define Your Data Models
In the previous step, you got your FastAPI server running and verified the auto-generated docs page. Now it's time to define the shape of your data.
Every REST API needs a clear contract for what data it accepts and returns. FastAPI uses Pydantic models to enforce this contract automatically. You define Python classes with type hints, and FastAPI handles validation, error messages, and documentation for you.
In this step, get ready to:
- Define Pydantic models for request validation and response serialization.
- Set up in-memory storage to simulate a database.
Create Pydantic models for task data
Your API needs three models: one for creating tasks, one for updating them with partial data, and one for the full task response that includes an ID.
- In your code editor, open main.py inside the task-manager-api folder.
- Replace the entire content of the file with the following code:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
app = FastAPI(title="Task Manager API")
class TaskCreate(BaseModel):
# Fields a client sends when creating a new task
title: str
description: str | None = None
completed: bool = False
What does this code do?
The import line now brings in HTTPException and status from fastapi. You'll use these in the next step for error handling and status codes.
The BaseModel import from pydantic is what makes your classes into data validators. Any class that inherits from BaseModel automatically validates incoming JSON against its field types.
In TaskCreate, title is required (no default), description is optional (defaults to None), and completed defaults to False. If a client sends a request missing title, FastAPI automatically returns a 422 validation error.
- Now add the TaskUpdate model below TaskCreate by pasting this code at the end of the file:
class TaskUpdate(BaseModel):
# All fields optional so clients can send partial updates
title: str | None = None
description: str | None = None
completed: bool | None = None
Why are all fields optional here?
TaskUpdate makes every field optional so clients can send only the fields they want to change. If a client only wants to mark a task as completed, they send {"completed": true} without needing to resend the title and description.
- Finally, add the Task response model below TaskUpdate by pasting this code at the end of the file:
class Task(BaseModel):
# The full task shape returned to clients (includes the ID)
id: int
title: str
description: str | None = None
completed: bool = False
What is the Task model for?
Task is the response model. It includes an id field that the server assigns when creating a task. Clients never send the ID. The server generates it and includes it in every response so clients can reference specific tasks later.
- Save main.py.
- Check the terminal where your dev server is running. It should auto-reload without errors.
Seeing a syntax error in the terminal?
Make sure each class definition starts with class ClassName(BaseModel): and all fields below it are indented with 4 spaces.
Check that you're using str | None (pipe syntax) and not Optional[str]. The pipe syntax requires Python 3.10 or higher.
help me fix a syntax error in my Pydantic models
Set up in-memory storage
Your endpoints need somewhere to store tasks. A Python dictionary works perfectly as a lightweight stand-in for a database during development.
- In main.py, find the line that says app = FastAPI(title="Task Manager API").
- Add the following code directly below that line (before the TaskCreate class):
# In-memory storage
tasks = {}
task_id_counter = 0
What does this code do?
tasks is a dictionary that stores tasks keyed by their ID. This gives you instant lookup by ID, just like a database primary key.
task_id_counter is a simple integer that increments each time you create a task, ensuring every task gets a unique ID. In a real app, the database would handle ID generation.
- Save main.py.
- Check the terminal. The server should auto-reload again with no errors.
Server crashed after adding storage?
Make sure the two storage lines sit between app = FastAPI(title="Task Manager API") and the class TaskCreate line. They should not be indented inside a class or function.
help me fix my FastAPI server crash after adding storage variables
✔️ Awesome, I've got everything!
Great. Double check you've saved your file and the dev server shows no errors in the terminal.
ⓧ I'd like to double check the full code
Here is the complete main.py so far:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
app = FastAPI(title="Task Manager API")
# In-memory storage
tasks = {}
task_id_counter = 0
class TaskCreate(BaseModel):
title: str
description: str | None = None
completed: bool = False
class TaskUpdate(BaseModel):
title: str | None = None
description: str | None = None
completed: bool | None = None
class Task(BaseModel):
id: int
title: str
description: str | None = None
completed: bool = False
Your data models are locked in. Next up, you'll use these models to build the Create and Read endpoints that actually accept and return task data.
Build the Create and Read Endpoints
Your data models are defined and ready to validate incoming data. Now you need actual endpoints that clients can call to create and retrieve tasks.
Every REST API needs a way to create resources and read them back. In this step, you'll build the three endpoints that handle the "C" and "R" of CRUD.
In this step, get ready to:
- Build a POST endpoint that creates new tasks.
- Build GET endpoints that retrieve tasks individually and as a list.
- Test the endpoints interactively using Swagger UI.
Build the POST endpoint to create tasks
The POST endpoint is where new tasks enter the system. It accepts a JSON body, validates it against your TaskCreate model, assigns a unique ID, and stores the task.
- In main.py, scroll to the bottom of the file (below the Task class definition).
- Add the POST endpoint by copying the following code at the bottom of the file:
@app.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
async def create_task(task: TaskCreate):
global task_id_counter
task_id_counter += 1
new_task = Task(id=task_id_counter, **task.model_dump())
tasks[task_id_counter] = new_task
return new_task
What does this code do?
- The @app.post decorator registers this function to handle POST requests at /tasks. The status_code=status.HTTP_201_CREATED tells FastAPI to return a 201 status code instead of the default 200, signaling that a resource was created.
- The task: TaskCreate parameter tells FastAPI to parse the request body as JSON and validate it against your TaskCreate model automatically.
- The global task_id_counter keyword lets the function modify the module-level counter. Each new task gets an incrementing ID.
- The task.model_dump() call converts the Pydantic model into a dictionary, which is then unpacked with ** to create a full Task instance including the generated id.
- Save main.py.
- Check your terminal. The dev server auto-reloads when you save, so you should see log lines confirming the server restarted.
- Refresh http://127.0.0.1:8000/docs in your browser. You should see a new green POST /tasks endpoint listed.
Don't see the new endpoint?
- Check your terminal for error messages. A syntax error in main.py prevents the server from reloading.
- Make sure you added the code below the class definitions, not inside one of them. The @app.post decorator must be at the module level (no indentation).
- Confirm you saved the file. Unsaved changes do not trigger a reload.
Help me debug why my POST endpoint isn't showing up in Swagger UI.
Build the GET endpoints to read tasks
Now you need a way to retrieve the tasks you create. You'll add two GET endpoints: one that returns all tasks and one that returns a single task by its ID.
- In main.py, find the line that says @app.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED).
- Add the following code above that line (between the Task class and the POST endpoint):
@app.get("/tasks", response_model=list[Task])
async def get_tasks():
return list(tasks.values())
What does this code do?
The @app.get("/tasks") decorator handles GET requests to /tasks. The response_model=list[Task] tells FastAPI to serialize the response as a list of Task objects. The function returns all stored tasks by converting the dictionary values to a list.
- Now add the single-task GET endpoint directly below the get_tasks function you just added (and still above the POST endpoint):
@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
return tasks[task_id]
What does this code do?
- The {task_id} in the path is a path parameter. FastAPI automatically converts it to an int because of the type hint task_id: int.
- If the task ID does not exist in the dictionary, the endpoint raises an HTTPException with a 404 status code. This returns a clear error message to the client instead of crashing.
- The detail parameter becomes the error message in the JSON response body.
- Save main.py.
- Refresh http://127.0.0.1:8000/docs. You should now see two blue GET /tasks and GET /tasks/{task_id} endpoints listed above the green POST endpoint.
Seeing an error in the terminal?
- Make sure the GET endpoints are placed above the POST endpoint, not inside it. Each function must start at the module level with no indentation.
- Check that HTTPException is in your import line at the top: from fastapi import FastAPI, HTTPException, status. This was added in a previous step, but confirm it is still there.
Help me fix an error with my GET endpoints in FastAPI.
Test the endpoints using Swagger UI
Your three endpoints are live. Time to test the full create-and-read flow using the interactive documentation.
- Open http://127.0.0.1:8000/docs in your browser.
- Click the POST /tasks endpoint to expand it.
- Click the Try it out button on the right.
- Replace the example JSON body with this:
{
"title": "Learn FastAPI",
"description": "Build a REST API project",
"completed": false
}
- Click Execute.
- Scroll down to the response. You should see a 201 status code and a response body that includes an id field (value 1) along with the task data you sent.
- Create a second task by clicking Execute again (you can change the title first if you like).
Now test the read endpoints.
- Click the GET /tasks endpoint, then Try it out, then Execute.
- You should see a 200 response containing a JSON array with both tasks you created.
- Click the GET /tasks/{task_id} endpoint and click Try it out.
- Enter 1 in the task_id field and click Execute. You should see the first task you created.
- Now enter 999 in the task_id field and click Execute.
- You should see a 404 response with the body {"detail": "Task not found"}.
Why does this matter?
Returning proper HTTP status codes (201 for created, 404 for not found) is what separates a production-quality API from a toy project. Clients rely on these codes to handle responses programmatically without parsing the body first.
✔️ Awesome, I've got everything!
Great. Double check you have saved main.py and all three endpoints are visible in Swagger UI.
ⓧ I'd like to double check the full code
Here is the complete main.py at this point:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
app = FastAPI(title="Task Manager API")
# In-memory storage
tasks = {}
task_id_counter = 0
class TaskCreate(BaseModel):
title: str
description: str | None = None
completed: bool = False
class TaskUpdate(BaseModel):
title: str | None = None
description: str | None = None
completed: bool | None = None
class Task(BaseModel):
id: int
title: str
description: str | None = None
completed: bool = False
@app.get("/tasks", response_model=list[Task])
async def get_tasks():
return list(tasks.values())
@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
return tasks[task_id]
@app.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
async def create_task(task: TaskCreate):
global task_id_counter
task_id_counter += 1
new_task = Task(id=task_id_counter, **task.model_dump())
tasks[task_id_counter] = new_task
return new_task
You now have a working create-and-read API. Next up, you'll build the update and delete endpoints to complete the full CRUD cycle.
Build the Update and Delete Endpoints
Your API can create and read tasks. Clients can add new tasks and retrieve them individually or as a list.
Now you need the other half of CRUD: updating existing tasks and deleting ones that are no longer needed. These two operations complete the lifecycle and give clients full control over their data.
In this step, get ready to:
- Build a PUT endpoint that partially updates task fields.
- Build a DELETE endpoint that removes tasks and returns the correct status code.
- Test a full create-update-delete cycle in Swagger UI.
Build the PUT endpoint to update tasks
When a client needs to change a task's title or mark it as completed, they send a PUT request with only the fields they want to change. Your endpoint needs to apply just those fields without overwriting everything else.
- In main.py, find the create_task function at the bottom of your file.
- Add the following PUT endpoint below it:
@app.put("/tasks/{task_id}", response_model=Task)
async def update_task(task_id: int, task_update: TaskUpdate):
# Return 404 if the task doesn't exist
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
existing_task = tasks[task_id]
# Get only the fields the client explicitly sent
update_data = task_update.model_dump(exclude_unset=True)
# Create a new task instance with those fields updated
updated_task = existing_task.model_copy(update=update_data)
tasks[task_id] = updated_task
return updated_task
What does this code do?
This endpoint handles partial updates. A client might send just {"completed": true} without touching the title or description.
- model_dump(exclude_unset=True) extracts only the fields the client included in their request. Fields they left out are excluded entirely.
- model_copy(update=update_data) creates a copy of the existing task with just those fields overwritten. Everything else stays the same.
- This two-step pattern is how production FastAPI apps handle partial updates safely.
Why exclude_unset instead of exclude_none?
If a client sends {"description": null}, they want to clear the description. Using exclude_unset=True preserves that intent because null was explicitly set.
Using exclude_none would incorrectly ignore that field, making it impossible for clients to deliberately remove a value.
- Save main.py.
- Your terminal should show that the FastAPI dev server auto-reloaded with no errors.
Seeing an indentation or syntax error?
- Make sure the @app.put decorator is at the same indentation level as your other decorators (no extra indent).
- Check that there is no leftover code between the create_task function and this new decorator.
- Confirm the function signature has both task_id: int and task_update: TaskUpdate as parameters.
Help me fix a syntax error in my FastAPI PUT endpoint
Build the DELETE endpoint to remove tasks
The DELETE endpoint is simpler. It removes a task from storage and returns no body. The HTTP standard uses status code 204 to signal a successful deletion with no content in the response.
- In main.py, add this DELETE endpoint below the update_task function you just wrote:
@app.delete("/tasks/{task_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_task(task_id: int):
# Return 404 if the task doesn't exist
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
# Remove the task from storage
del tasks[task_id]
What does this code do?
- status.HTTP_204_NO_CONTENT tells FastAPI to return a 204 status code. This means the deletion succeeded but there is nothing to send back in the response body.
- del tasks[task_id] removes the task from the in-memory dictionary permanently.
- The 404 check runs first so clients get a clear error if they try to delete a task that doesn't exist.
- Save main.py.
- Your terminal should show the server reloading successfully again.
Server not reloading?
- Check that your fastapi dev process is still running in the terminal. If it crashed earlier, restart it with fastapi dev main.py.
- Make sure the @app.delete decorator is flush left (no indentation inside another function).
Help me fix my FastAPI DELETE endpoint
✔️ Awesome, I've got everything!
Great. Double check you've saved main.py and confirmed the server reloaded without errors.
ⓧ I'd like to double check the full code
Here is the complete main.py file with all endpoints so far:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
app = FastAPI(title="Task Manager API")
# In-memory storage
tasks = {}
task_id_counter = 0
class TaskCreate(BaseModel):
title: str
description: str | None = None
completed: bool = False
class TaskUpdate(BaseModel):
title: str | None = None
description: str | None = None
completed: bool | None = None
class Task(BaseModel):
id: int
title: str
description: str | None = None
completed: bool = False
@app.get("/tasks", response_model=list[Task])
async def get_tasks():
return list(tasks.values())
@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
return tasks[task_id]
@app.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
async def create_task(task: TaskCreate):
global task_id_counter
task_id_counter += 1
new_task = Task(id=task_id_counter, **task.model_dump())
tasks[task_id_counter] = new_task
return new_task
@app.put("/tasks/{task_id}", response_model=Task)
async def update_task(task_id: int, task_update: TaskUpdate):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
existing_task = tasks[task_id]
update_data = task_update.model_dump(exclude_unset=True)
updated_task = existing_task.model_copy(update=update_data)
tasks[task_id] = updated_task
return updated_task
@app.delete("/tasks/{task_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_task(task_id: int):
if task_id not in tasks:
raise HTTPException(status_code=404, detail="Task not found")
del tasks[task_id]
Test the full CRUD cycle in Swagger UI
With all four operations built, you can now run through an entire task lifecycle. This proves your API handles every stage correctly.
- Open http://127.0.0.1:8000/docs in your browser to return to the Swagger UI.
- Expand the POST /tasks section and click Try it out.
- Create a task with a title like "Buy groceries" and click Execute.
- Confirm the response shows status code 201 and the task has an id of 1.
- Expand the PUT /tasks/{task_id} section and click Try it out.
- Enter 1 for the task_id path parameter.
- In the request body, enter {"completed": true} to mark the task as done.
- Click Execute and confirm the response shows "completed": true while the title remains unchanged.
- Expand the DELETE /tasks/{task_id} section and click Try it out.
- Enter 1 for the task_id and click Execute.
- Confirm the response shows status code 204 with no response body.
- Now test the 404 case. Expand GET /tasks/{task_id} and try to retrieve task 1 again.
- Confirm the response shows status code 404 with the detail "Task not found".
What should I see?
You just walked through the full lifecycle of a task: created it (201), updated it (200), deleted it (204), and confirmed it's gone (404). Each status code tells the client exactly what happened without ambiguity.
PUT returning all fields as null?
- Make sure you used model_copy(update=update_data) and not Task(**update_data). The second version creates a brand new task from scratch, leaving unset fields as their defaults.
- Check that exclude_unset=True is spelled exactly as shown. A typo here means all fields (including unset ones) get included as None.
Help me debug my PUT endpoint returning null values
Your API now supports the full CRUD cycle. Next up, you'll tackle the Secret Mission and add filtering and sorting to make your task list endpoint even more powerful.
Secret mission
Add Filtering and Sorting
Your API returns every task every time. Real-world APIs let clients ask for exactly the data they need. Can you add query parameters that filter tasks by completion status and sort them alphabetically by title?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The task-manager-api project directory (contains your code, virtual environment, and installed packages).
- The FastAPI development server (running process).
Keep everything running
No action needed. Choose this if you're still actively building or want to keep experimenting with your API.
- Your project directory stays on your Desktop.
- If the dev server is still running, it will continue serving requests at http://127.0.0.1:8000.
- To restart the server later, activate your virtual environment and run fastapi dev main.py from inside the project folder.
Pause - I'll come back to this later
Shut down the running server to free up your terminal. Your files stay intact for next time.
- Switch to the terminal where the FastAPI server is running.
- Press Ctrl+C to stop the server.
- Deactivate your virtual environment by running:
deactivate
To resume later, navigate back to your task-manager-api folder, reactivate the virtual environment, and start the server again.
Delete - I don't want to use this again
Remove all project files and start fresh. This deletes your code, virtual environment, and all installed packages.
- Switch to the terminal where the FastAPI server is running.
- Press Ctrl+C to stop the server.
- Deactivate your virtual environment by running:
deactivate
- Navigate to your Desktop and delete the project folder.
macOS
cd ~/Desktop
rm -rf task-manager-api
Windows
cd ~\Desktop
rmdir /s /q task-manager-api
- Confirm the folder is gone by running ls (macOS) or dir (Windows). You should no longer see task-manager-api in the output.
Nice Work!
Nice Work!
You just built a fully functional REST API from scratch using FastAPI. Your Task Manager API handles all four CRUD operations with automatic validation, proper error handling, and interactive documentation.
Here's what you learned:
- Built a complete REST API with all CRUD operations (Create, Read, Update, Delete) using proper HTTP status codes like 201 Created, 204 No Content, and 404 Not Found.
- Used Pydantic models for automatic request validation and clean data serialization, including partial updates with model_dump(exclude_unset=True) and model_copy(update=...).
- Generated interactive Swagger UI documentation automatically with zero extra code, and used it to test every endpoint live in your browser.
- Added query parameter filtering and sorting to handle real-world data access patterns in your Secret Mission.
Ready to quiz yourself?