Building a Food Recall Tracking App with Python, FastAPI, and MongoDB (Part 0)
Food recalls impact our lives a major ways. Starting with our health when we consume them the food before it's recalled, to our mental health when we lose trust in the systems we expect to protect us, and with the loss of jobs when factories or members of the supply chain close due to the impact the recalls have on their business. Tracking and understanding these recalls can be challenging. Tracking and understanding these recalls is important. This multipart tutorial series will guide you through creating a food recall tracking tool that leverages the FDA's food recall API. In this first part, we'll focus on building the backend API with Python, FastAPI, and MongoDB to pull and store data from the FDA API. Future installments will cover the frontend, built using Next.js, and additional features like machine learning and conversational interfaces.
Why Python and FastAPI?
Python + FastAPI
FastAPI: a modern web framework for building Python APIs. It is:
- Fast: Built on Starlette and Pydantic, it offers excellent performance.
- Easy to use: Automatic data validation and documentation.
- Scalable: Asynchronous support makes it well-suited for high-concurrency applications.
Other Python API libraries, like Flask or Django REST Framework, are powerful but may require additional configuration for asynchronous support and automatic validation.
Why Not Node.js or Full-Stack Next.js?
While Node.js or a full-stack Next.js app could also handle this project, Python's robust data-processing libraries make it ideal for adding machine learning (ML) capabilities later. With Python, I can integrate ML models to analyze and predict trends in food recalls or implement retrieval-augmented generation (RAG) systems to enable conversational interactions.
All that being true, I chose Python because I wanted to build something with Python. I've been intrigued by the tool for years and I've built plenty of tools with Node.js. I'm excited to learn more about Python and this won't be the last thing I build with it.
Backend Architecture Overview
The backend will:
- Fetch data from the FDA API: Regularly query the FDA's food enforcement API for updates on food recalls.
- Store data in MongoDB: Use MongoDB to persist food recall data, ensuring users can query historical data.
- Expose API endpoints: Provide endpoints for manual updates and status checks.
Technologies used:
- Python: Core programming language.
- FastAPI: Framework for building the API.
- MongoDB: NoSQL database to store food recall data.
- Requests: Library for making HTTP calls to the FDA API.
- Schedule: Library to automate weekly data-fetching tasks.
Step-by-Step Guide
Step 1: Set Up the Environment
Create a Virtual Environment
Using a virtual environment helps isolate your project’s dependencies.
# Create and activate a virtual environment
python -m venv tbcvenv # venv initiates the virtual environment and tbcvenv is the name of the virtual environment. You may use any name you choose
source tbcvenv/bin/activate # On Windows: venv\Scripts\activate
Install Required Libraries
We’ll use the following dependencies:
- FastAPI: The framework to create our API.
- Uvicorn: ASGI server to run the FastAPI application.
- pymongo: Python driver for MongoDB.
- python-dotenv: To manage environment variables.
- requests: To fetch data from the FDA API.
- schedule: For automating periodic tasks.
Install them with:
pip install fastapi uvicorn pymongo python-dotenv requests schedule
Step 2: Start with a Simple API
Create a file named main.py
and write a very basic FastAPI application:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Welcome to the FDA Enforcement Data API."}
Explanation:
-
FastAPI: Provides the
FastAPI
class to define the app and the@app.get
decorator to define endpoints. - Root Endpoint: Displays a simple welcome message.
Run the server with:
uvicorn main:app --reload
Visit http://127.0.0.1:8000/ to see the message.
Step 3: Connect to MongoDB
Add MongoDB support by installing and using pymongo
. Update main.py
:
from fastapi import FastAPI
from pymongo import MongoClient
load_dotenv()
app = FastAPI()
# MongoDB connection
# mongodb+srv://<username>:<password>@palabras-express-api.whbeh.mongodb.net/?retryWrites=true&w=majority&
MONGO_URI = os.getenv("MONGO_URI")
DB_NAME = os.getenv("DB_NAME")
COLLECTION_NAME = os.getenv
client = MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]
## Replace
<username>:<password> with your username and password.
@app.get("/")
def root():
return {"message": "Welcome to the FDA Enforcement Data API."}
Explanation:
- pymongo: Connects to the MongoDB server.
- MongoClient: Initializes the client using the MongoDB URI.
-
Database/Collection: We define
fda_db
as the database andenforcements
as the collection for storing data.
Make sure MongoDB is running. You may connect to MongoDB locally or use a cloud-hosted MongoDB instance (e.g., MongoDB Atlas). I'm using MongoDB Atlas.
Step 4: Fetch Data from the FDA API
Update main.py
to fetch data using requests
:
import requests
from fastapi import FastAPI
from pymongo import MongoClient
import os
from dotenv import load_dotenv
FDA_API_URL = "https://api.fda.gov/food/enforcement.json"
FDA_API_QUERY = {"search": 'distribution_pattern:"nationwide"', "limit": 5}
load_dotenv()
app = FastAPI()
# MongoDB connection
# mongodb+srv://<username>:<password>@palabras-express-api.whbeh.mongodb.net/?retryWrites=true&w=majority&
MONGO_URI = os.getenv("MONGO_URI")
DB_NAME = os.getenv("DB_NAME")
COLLECTION_NAME = os.getenv("COLLECTION_NAME")
client = MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]
@app.get("/")
def root():
return {"message": "Welcome to the FDA Enforcement Data API."}
@app.get("/update-data")
def update_data():
response = requests.get(FDA_API_URL, params=FDA_API_QUERY)
data = response.json()
return data
Explanation:
- requests: Fetches data from the FDA API.
- FDA_API_URL: Endpoint to retrieve food recall data.
- FDA_API_QUERY: Parameters for the API call.
Visit http://127.0.0.1:8000/update-data to see the data fetched from the FDA API.
Step 5: Store Data in MongoDB
Extend /update-data
to store the fetched data in MongoDB:
import requests
from fastapi import FastAPI
from pymongo import MongoClient
import os
from dotenv import load_dotenv
FDA_API_URL = "https://api.fda.gov/food/enforcement.json"
FDA_API_QUERY = {"search": 'distribution_pattern:"nationwide"', "limit": 5}
load_dotenv()
app = FastAPI()
# MongoDB connection
# mongodb+srv://<username>:<password>@palabras-express-api.whbeh.mongodb.net/?retryWrites=true&w=majority&
MONGO_URI = os.getenv("MONGO_URI")
DB_NAME = os.getenv("DB_NAME")
COLLECTION_NAME = os.getenv("COLLECTION_NAME")
client = MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]
@app.get("/")
def root():
return {"message": "Welcome to the FDA Enforcement Data API."}
@app.get("/update-data")
def update_data():
response = requests.get(FDA_API_URL, params=FDA_API_QUERY)
data = response.json()
for record in data.get("results", []):
collection.update_one(
{"_id":
record["recall_number"]},
{"$set": record},
upsert=True
)
return {"message": "Data successfully updated."}
Explanation:
-
MongoDB Storage: Each record uses
recall_number
as a unique identifier (_id
). - Upsert: Updates an existing record or inserts it if it doesn’t exist.
Step 6: Automate Updates with schedule
Add a scheduler to automate data fetching weekly:
import requests
from fastapi import FastAPI
from pymongo import MongoClient
import os
from dotenv import load_dotenv
import schedule
import time
# Load environment variables from a .env file (if it exists)
load_dotenv()
app = FastAPI()
# MongoDB configuration using environment variables
# mongodb+srv://<username>:<password>@palabras-express-api.whbeh.mongodb.net/?retryWrites=true&w=majority&
MONGO_URI = os.getenv("MONGO_URI")
DB_NAME = os.getenv("DB_NAME")
COLLECTION_NAME = os.getenv("COLLECTION_NAME")
# API endpoint
FDA_API_URL = "https://api.fda.gov/food/enforcement.json"
FDA_API_QUERY = {"search": 'distribution_pattern:"nationwide"', "limit": 15}
# Connect to MongoDB
client = MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]
def fetch_and_update_data():
"""
Fetch data from the FDA API and update MongoDB.
"""
try:
response = requests.get(FDA_API_URL, params=FDA_API_QUERY)
response.raise_for_status() # Raise an error for bad status codes
data = response.json()
# Insert or update records in MongoDB
for record in data.get("results", []):
collection.update_one(
{"_id": record["recall_number"]}, # Use recall_number as unique ID
{"$set": record},
upsert=True
)
print("Data successfully updated.")
except Exception as e:
print(f"Error fetching data: {e}")
# Schedule the task to run once a week
schedule.every().week.do(fetch_and_update_data)
# Background job for running the schedule
def run_schedule():
while True:
schedule.run_pending()
time.sleep(1)
@app.get("/")
def root():
"""
Root endpoint to provide a welcome message.
"""
return {"message": "Welcome to the FDA Enforcement Data API. Visit /update-data to manually update data."}
# FastAPI route to trigger data update manually
@app.get("/update-data")
def update_data():
"""
Endpoint to trigger data fetching and updating manually.
"""
fetch_and_update_data()
return {"message": "Data update triggered."}
if __name__ == "__main__":
import threading
# Run the scheduler in a separate thread
threading.Thread(target=run_schedule, daemon=True).start()
# Start FastAPI
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
Explanation:
-
schedule: Schedules tasks like
update_data
to run weekly. - Threading: Runs the scheduler alongside the FastAPI server.
Conclusion
This completes the first part of building the backend for a food recall tracking tool. We:
- Created a basic FastAPI application.
- Connected it to MongoDB.
- Pulled and stored data from the FDA API.
- Automated data fetching with
schedule
.
In the next part, we’ll build a frontend using Next.js to display and interact with the data. Stay tuned!
Did you enjoy reading this tutorial? Do you have any questions or need help using it? What would you like to learn next?
Let me know in the comments below! Or use the chat bubble in the lower right to chat in real time. Schedule an appointment to talk and get a free hour of consulting using the chat bubble in the lower right corner of the screen.