Module 04 / Project 03 — Database-Backed API¶
Home: README
Learn Your Way¶
| Read | Build | Watch | Test | Review | Visualize | Try |
|---|---|---|---|---|---|---|
| — | This project | — | — | Flashcards | — | — |
Focus¶
SQLite + SQLAlchemy ORM, database models, FastAPI dependency injection with Depends().
Why this project exists¶
The todo API from Project 02 loses all data when the server restarts because it uses an in-memory list. This project replaces that list with a real SQLite database using SQLAlchemy ORM. You will learn how to define database models, create tables, manage database sessions with FastAPI's dependency injection system, and persist data across server restarts.
Run¶
Then open http://127.0.0.1:8000/docs to test the endpoints. Create some todos, stop the server with Ctrl+C, start it again, and confirm your todos are still there.
A todos.db file will appear in the project directory. This is your SQLite database.
Expected output¶
The API behaves identically to Project 02, but data persists:
# Create a todo
curl -X POST http://127.0.0.1:8000/todos -H "Content-Type: application/json" -d '{"title": "Survive a restart"}'
# Returns: {"id": 1, "title": "Survive a restart", "completed": false, "created_at": "2024-01-15T10:30:00"}
# Stop the server (Ctrl+C), then restart it (python app.py)
# Data is still there
curl http://127.0.0.1:8000/todos
# Returns: [{"id": 1, "title": "Survive a restart", "completed": false, "created_at": "2024-01-15T10:30:00"}]
Alter it¶
- Add a
prioritycolumn (integer, default 0) to the Todo model. Update the schemas to include it in create and response models. - Add a
GET /todos?completed=truequery parameter to filter todos by completion status. - Add a
GET /todos/countendpoint that returns{"total": N, "completed": M, "pending": P}.
Break it¶
- Delete the
todos.dbfile while the server is running, then try to create a todo. What happens? - Add a new column to the SQLAlchemy model but do not delete the old database. Start the server and try to use the new column. What error do you get?
- Remove the
yieldfrom theget_dbdependency and return the session directly. Create a few todos, then check if the database is growing correctly.
Fix it¶
- Restart the server. SQLAlchemy recreates the database file and tables on startup because of
Base.metadata.create_all(). - Delete
todos.dband restart the server. In production, you would use a migration tool like Alembic instead of deleting the database. - Restore the
yieldand add thefinallyblock that closes the session. Without proper cleanup, database connections leak and the application eventually crashes.
Explain it¶
- What is an ORM? How does the SQLAlchemy
Todoclass relate to a database table? - What does
Depends(get_db)do? Why is it better than creating a database session inside each endpoint function? - What is the difference between the SQLAlchemy model (
models.py) and the Pydantic schema (schemas.py)? Why do you need both? - What does
yielddo in theget_dbfunction? What happens in thefinallyblock and why is it important?
Mastery check¶
You can move on when you can:
- explain the difference between a SQLAlchemy model and a Pydantic schema,
- describe what
Depends()does and why FastAPI uses it, - add a new column to the model and update all layers (model, schema, endpoint),
- delete the database and recover by restarting the server.
Related Concepts¶
Next¶
Continue to 04-authentication.