This project follows a clean, layered architecture that separates concerns across four distinct tiers. An incoming HTTP request is first received by a route handler, which delegates business logic to a controller. The controller validates data against Pydantic models, queries MongoDB via PyMongo, and passes raw documents through schema serializers before returning a well-typed response to the client. This separation keeps each layer focused and independently testable.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tutosrive/fastapi-CRUD-MongoDB/llms.txt
Use this file to discover all available pages before exploring further.
Directory Layout
Layer-by-Layer Breakdown
main.py — App Entry Point & Router Registration
main.py — App Entry Point & Router Registration
main.py is the top-level entry point for the FastAPI application. It creates the FastAPI instance, configures the app’s metadata (title, description, version, and OpenAPI tag groupings), and registers the three routers — home_router, user_router, and task_router — via app.include_router().openapi_tags list (sourced from app/config/docs.py) controls how endpoint groups appear in the Swagger UI.app/config/db.py — MongoDB Connection
app/config/db.py — MongoDB Connection
This module establishes the single shared PyMongo connection used throughout the application. Calling The
MongoClient() with no arguments connects to a MongoDB instance running on localhost:27017 — the default development setup.mong_conn object is imported wherever a database operation is needed. Collections are accessed as mong_conn.local.user and mong_conn.local.task, both residing in MongoDB’s built-in local database.app/config/docs.py — OpenAPI Tag Metadata
app/config/docs.py — OpenAPI Tag Metadata
docs.py defines the api_tags list that is passed to the FastAPI() constructor. Each entry provides a name and description that FastAPI uses to group and label endpoints in the auto-generated Swagger UI (/docs) and ReDoc (/redoc) interfaces.main.py and makes it easy to expand descriptions or add new resource groups.app/models/ — Pydantic Request & Response Models
app/models/ — Pydantic Request & Response Models
The
models/ directory contains Pydantic BaseModel classes that define the shape of data flowing into and out of the API. FastAPI uses these models automatically for request body parsing, response serialization, and JSON Schema generation in the OpenAPI docs.user.pydefines theUsermodel with fields:id(optional string),name,age, andemail.task.pydefinesTask(for creation) andTaskUpdate(for partial updates via PUT), whereTaskUpdatemarks every field asOptionalto allow clients to send only the fields they wish to change.
app/schemas/ — MongoDB Document Serializers
app/schemas/ — MongoDB Document Serializers
The
schemas/ directory contains plain functions that convert raw MongoDB documents (Python dicts with an _id key containing a BSON ObjectId) into clean Python dicts whose id field is a plain string. This translation step is necessary because BSON ObjectId objects are not JSON-serializable and do not match the Optional[str] type declared on Pydantic models.user.pyexportsuser_entity()(single document) andusers_entity()(list).task.pyexportstask_entity(),tasks_entity(), andtask_update_entity(). Thetask_update_entity()function additionally strips anyNonevalues from the update dict so that only provided fields are passed to MongoDB’s$setoperator.
app/controllers/ — Business Logic
app/controllers/ — Business Logic
Each controller is a class (
UserController, TaskController) that encapsulates all direct MongoDB operations for its resource. Controllers import mong_conn from app/config/db.py, the relevant Pydantic models from app/models/, and the serializer functions from app/schemas/.Implemented methods follow a consistent pattern:get_all_*()— calls.find()and passes the cursor to the list serializer.get_*(id)— calls.find_one()with anObjectIdfilter; raises HTTP 404 if not found.add_*(obj)— calls.insert_one(), then re-fetches the inserted document byinserted_idand returns the serialized entity.update_*(id, obj)— calls.find_one_and_update()with$set; re-fetches viaget_*()to return the updated state.delete_*(id)— calls.find_one_and_delete(); returns HTTP 204 No Content on success.
__raise_404() helper is used across all not-found branches to keep error handling consistent.app/routes/ — APIRouter Endpoint Definitions
app/routes/ — APIRouter Endpoint Definitions
Route files wire HTTP methods and URL paths to controller calls. Each file creates an
APIRouter with a prefix (e.g., /users, /tasks) and a tags list for OpenAPI grouping, then defines endpoint functions decorated with @router.get, @router.post, @router.put, and @router.delete.Route functions are intentionally thin: they receive the request data, pass it to the controller, and return the result. All HTTPException instances raised by controllers are caught and re-raised so FastAPI can convert them to the appropriate HTTP error responses.Request Flow
The following sequence describes the complete lifecycle of a single API request through the application:- HTTP request arrives at a URL such as
POST /users/. - FastAPI matches the route in
app/routes/user.pyand invokes theadd_user()handler. - The handler calls the controller:
user_ctrl.add_user(user.model_dump())passes a plain dict toUserController.add_user(). - The controller writes to MongoDB:
mong_conn.local.user.insert_one(user)inserts the document and returns anInsertOneResult. - The controller re-fetches the document using the
inserted_idto ensure the response reflects the stored state. - The schema serializer runs:
user_entity(__user)converts the raw MongoDB dict (with_idasObjectId) into a clean dict (withidasstr). - FastAPI serializes the response against the
response_model=Userdeclaration and sends a JSON response to the client.
The collections
mong_conn.local.user and mong_conn.local.task both reside in MongoDB’s built-in local database. This database is always present on any MongoDB instance, which makes it convenient for development — but it is not replicated in a replica set. For any production or staging deployment, replace local with a dedicated application database name (see the Configuration guide).