Build a Flask Inventory Reports API

Connect Flask to Firestore and build CSV export and alert endpoints.

Introduction

30 Second Summary

You have a working inventory app, but your team needs downloadable reports and alerts when stock runs low. Your frontend JavaScript can display data, but it cannot generate files or process numbers across hundreds of items.

In this project, you will build a Flask REST API that connects to your existing Firestore inventory database and provides three reporting endpoints your frontend can call.

What You'll Build

A Python backend API that reads live inventory data from Firestore and serves it through endpoints for listing, alerts, and CSV export.

By the end of this project, you'll have:

  • A running Flask application with three API endpoints for inventory listing, low-stock alerts, and CSV export.
  • A Firebase Admin SDK integration that reads live data from your existing Firestore database using Python.
  • A working CORS configuration and fetch integration connecting your existing frontend to the Flask backend.
  • Secret Mission: Add a summary analytics endpoint that calculates total stock value and category breakdowns.

Are there any prerequisites?

This project assumes you have basic Flask experience (routing, running a dev server) and an existing Firebase project on the Blaze plan with Firestore inventory data. You should also have Python 3.12 and VS Code installed.

Set Up Your Flask Project

Your Firebase frontend handles real-time CRUD operations well, but some tasks belong on a server. Generating downloadable reports, filtering data with business logic, and processing exports all require a backend.

In this step, you will create a Python project folder, install Flask and its dependencies, and get a basic API server running locally.

In this step, get ready to:
  • Set up a Python virtual environment with Flask and its dependencies installed.
  • Download your Firebase service account key.
  • Create a basic Flask app and verify it runs.
Create a project folder and virtual environment

A virtual environment isolates your project's Python packages from your system-wide installation. This prevents version conflicts between projects.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir inventory-reports
cd inventory-reports
  • Create a virtual environment by running this command:

macOS

python3 -m venv .venv

Windows

py -3 -m venv .venv
  • Activate the virtual environment by running:

macOS

source .venv/bin/activate

Windows

.venv\Scripts\activate

Your terminal prompt now shows (.venv) at the beginning. This confirms the virtual environment is active.

  • Install the project dependencies by running:
pip install Flask==3.1.3 firebase-admin==7.4.0 flask-cors==6.0.5

What does this command do?

  • Flask==3.1.3 installs the Flask web framework at a specific version.
  • firebase-admin==7.4.0 installs the Firebase Admin SDK, which lets Python talk directly to your Firestore database.
  • flask-cors==6.0.5 installs the CORS extension so your frontend can call the API later.

Seeing a permission error or install failure?

Make sure your terminal prompt starts with (.venv). If it does not, re-run the activation command above. Installing packages outside the virtual environment can cause permission errors.

help me fix a pip install error in my virtual environment

  • Create a requirements.txt file to record the pinned versions by running:

macOS

printf 'Flask==3.1.3\nfirebase-admin==7.4.0\nflask-cors==6.0.5\n' > requirements.txt

Windows

@(
"Flask==3.1.3",
"firebase-admin==7.4.0",
"flask-cors==6.0.5"
) | Set-Content requirements.txt
  • Confirm the file was created by running:
ls

You should see requirements.txt and the .venv folder listed in the output.

Why pin versions?

Pinning versions in requirements.txt ensures anyone who runs your project installs the exact same package versions you used. This prevents subtle bugs from version mismatches.

Download your Firebase service account key

The Firebase Admin SDK needs a service account key to authenticate your Python backend with Firestore. This key grants admin-level read/write access to your database.

  • Open the Firebase Console for your existing project.
  • Click the gear icon next to Project Overview to open Project Settings.
  • Select the Service accounts tab.
  • Click Generate new private key.
  • A confirmation dialog appears. Click Generate key to download the JSON file.
  • Rename the downloaded file to serviceAccountKey.json and move it into your inventory-reports folder on your Desktop.

Why not commit this file to version control?

This JSON file contains private credentials that grant full access to your Firestore data. Never commit it to Git or share it publicly. For this project, keeping it in your local folder is fine.

  • Verify the file is in the right place by running:
ls serviceAccountKey.json

You should see serviceAccountKey.json in the output.

File not found?

Make sure you renamed the downloaded file to exactly serviceAccountKey.json and placed it inside the inventory-reports folder on your Desktop. The file name is case-sensitive.

help me find my Firebase service account key file

Create the Flask application and verify it runs

With your dependencies installed and credentials in place, you can create the actual Flask application file.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Select the inventory-reports folder on your Desktop, then click Open.
  • Create a new file called app.py by clicking the new file icon in the Explorer sidebar.
  • Add the following code to app.py:
from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


if __name__ == "__main__":
    app.run(debug=True)

What does this code do?

  • app = Flask(__name__) creates a Flask application instance. Flask uses the module name to locate resources.
  • @app.route("/api/health", methods=["GET"]) registers a URL path that responds to GET requests.
  • jsonify({"status": "healthy"}) returns a JSON response with the correct content-type header.
  • app.run(debug=True) starts the Flask development server with auto-reload enabled.
  • Save app.py.
  • Return to your terminal (make sure (.venv) is still showing) and start the Flask server by running:
python app.py

You should see output indicating the server is running on http://127.0.0.1:5000.

  • Open your browser and visit http://127.0.0.1:5000/api/health.

You should see {"status": "healthy"} displayed in your browser.

Don't see the JSON response?

Make sure your virtual environment is active ((.venv) in the prompt) before running python app.py. If you see a ModuleNotFoundError for flask, the virtual environment is not active.

Check that you saved app.py in the inventory-reports folder (not a subfolder or parent folder).

help me troubleshoot my Flask server not starting

✔️ Awesome, I've got everything!

Great. Double check you have saved both files.

ⓧ I'd like to double check the full code

Here is the complete app.py file:

from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


if __name__ == "__main__":
    app.run(debug=True)

Here is the complete requirements.txt file:

Flask==3.1.3
firebase-admin==7.4.0
flask-cors==6.0.5

Your Flask project is set up and running. Next up, you will connect it to your Firestore database and start reading live inventory data.

Connect Flask to Firestore

Your Flask app is running and healthy. Now it is time to connect it to your real data.

The Firebase Admin SDK lets your Python backend read directly from your existing Firestore database. In this step, you will initialize the SDK with your service account credentials and create your first data endpoint.

In this step, get ready to:
  • Initialize the Firebase Admin SDK in your Flask app.
  • Create the /api/inventory endpoint to read Firestore documents.
  • Test the endpoint and verify your real inventory data comes through.
Initialize the Firebase Admin SDK

Before your Flask app can read Firestore data, it needs to authenticate with Firebase using the service account key you downloaded in Step 1.

  • In app.py, add the Firebase imports and SDK initialization code. Update the top of your file so it looks like this:
from flask import Flask, jsonify
import firebase_admin
from firebase_admin import credentials, firestore

app = Flask(__name__)

# Load the service account credentials
cred = credentials.Certificate("serviceAccountKey.json")
# Initialize the Firebase Admin SDK
firebase_admin.initialize_app(cred)
# Create a Firestore database client
db = firestore.client()

What does this code do?

The two new import lines bring in the Firebase Admin SDK and its credential/Firestore helpers.

  • credentials.Certificate("serviceAccountKey.json") loads your private key so Python can authenticate as an admin.
  • firebase_admin.initialize_app(cred) boots the SDK with those credentials.
  • firestore.client() returns a client object you will use to query your database throughout the app.
  • Save app.py.

Seeing an import error?

  • Make sure your virtual environment is active. You should see (.venv) at the start of your terminal prompt.
  • Confirm firebase-admin is installed by running pip list and checking the output.

Why is Firebase not importing correctly?

Create the /api/inventory endpoint

With the SDK initialized, you can now query your Firestore collection. This endpoint streams every document and returns the data as a JSON array.

  • In app.py, find the if __name__ == "__main__": block at the bottom. Add the following route above it:
@app.route("/api/inventory", methods=["GET"])
def get_inventory():
    # Stream all documents from the inventory collection
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        # Convert each document to a dictionary
        item = doc.to_dict()
        # Include the document ID in the response
        item["id"] = doc.id
        items.append(item)
    return jsonify(items)

What does this code do?

  • db.collection("inventory").stream() fetches every document from your Firestore inventory collection as a stream of snapshots.
  • doc.to_dict() converts each Firestore document into a standard Python dictionary.
  • doc.id grabs the auto-generated document ID so your frontend can reference specific items later.
  • jsonify(items) serializes the full list into a JSON HTTP response.

Using a different collection name?

If your Firestore collection is not called inventory, replace "inventory" in the db.collection() call with your actual collection name.

  • Save app.py.

Seeing a credentials error on save?

  • Check that serviceAccountKey.json is in the same folder as app.py. The file name must match exactly, including capitalization.
  • Make sure the JSON file is the original download from Firebase, not an empty or renamed placeholder.

Help me fix my Firebase credentials error in Flask.

✔️ Awesome, I've got everything!

Great. Double check you have saved app.py before moving on.

ⓧ I'd like to double check the full code

Here is the complete app.py at this point:

from flask import Flask, jsonify
import firebase_admin
from firebase_admin import credentials, firestore

app = Flask(__name__)

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)
db = firestore.client()


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


@app.route("/api/inventory", methods=["GET"])
def get_inventory():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)
    return jsonify(items)


if __name__ == "__main__":
    app.run(debug=True)
Test the endpoint

Time to see your real Firestore data flowing through your Flask API.

  • If your Flask server is still running, stop it by pressing Ctrl+C in the terminal.
  • Restart the server by running:
python app.py
  • Open your browser and visit http://127.0.0.1:5000/api/inventory.

You should see a JSON array containing your actual inventory documents from Firestore, with all fields visible and each item including its document id.

Seeing an empty array or error?

  • An empty [] means the collection name in your code does not match your Firestore collection. Check the exact name in the Firebase Console.
  • A 500 error usually means serviceAccountKey.json is missing or malformed. Open the file and confirm it contains valid JSON with a project_id field.
  • If you see a permissions error, confirm your Firebase project is on the Blaze plan and that the service account has Firestore access.

My /api/inventory endpoint is not returning my Firestore data. Help me debug.

Your Flask app can now read live data from Firestore. Next, you will build a low-stock alerts endpoint that filters this data and returns only the items that need attention.

Build the Low-Stock Alerts Endpoint

Your Flask app can already read inventory data from Firestore and return it as JSON. Now you need something more useful: a way to find items that are running low.

In this step, you will build an endpoint that filters your inventory data and returns only items at or below a stock threshold. The threshold is configurable through the URL, so your frontend can request different alert levels without changing any backend code.

In this step, get ready to:
  • Add a low-stock alerts route to your Flask app.
  • Accept a configurable threshold from the URL query string.
  • Test the endpoint with different threshold values.
Create the /api/alerts/low-stock endpoint

Query parameters let callers pass options through the URL itself. Your endpoint will read a threshold query parameter so the frontend can decide what counts as "low stock" without you hard-coding a number.

  • In app.py, find the import line at the very top of the file:
from flask import Flask, jsonify
  • Add request to the import so the line now looks like this:
from flask import Flask, jsonify, request

What is request?

Flask's request object gives you access to everything the caller sent: URL parameters, form data, headers, and more. You will use request.args to read query string values from the URL.

  • Now scroll down to the end of the get_inventory function. Find this line:
    return jsonify(items)
  • Add the low-stock alerts endpoint below that function (above the if __name__ block) by copying this code:
@app.route("/api/alerts/low-stock", methods=["GET"])
def low_stock_alerts():
    # Read the threshold from the URL query string (default: 10)
    threshold = request.args.get("threshold", default=10, type=int)
    # Fetch all inventory documents from Firestore
    docs = db.collection("inventory").stream()
    alerts = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        # Only keep items at or below the threshold
        if item.get("quantity", 0) <= threshold:
            alerts.append(item)
    # Sort by quantity so the lowest-stock items appear first
    alerts.sort(key=lambda x: x.get("quantity", 0))
    return jsonify({"threshold": threshold, "count": len(alerts), "items": alerts})

What does this code do?

This endpoint combines three data-processing steps that would be awkward to do in frontend JavaScript alone:

  • request.args.get("threshold", default=10, type=int) reads the threshold value from the URL. If the caller does not provide one, it defaults to 10.
  • The for loop fetches every document and keeps only items whose quantity is at or below the threshold.
  • alerts.sort() orders the filtered results from lowest quantity to highest, so the most urgent items appear first.
  • The response JSON includes the threshold used, a count of matching items, and the filtered items list.
  • Save app.py.

Why filter in Python instead of Firestore?

Firestore supports server-side queries, but filtering in Python keeps this project simple. It avoids Firestore composite index requirements and lets you practice Python data processing. For small to medium inventory datasets, this approach works well.

Seeing a NameError for request?

Make sure you added request to the Flask import line at the top of app.py. The line should read from flask import Flask, jsonify, request.

help me fix a NameError for request in my Flask app

Test with different thresholds

Your endpoint accepts an optional query parameter in the URL. Testing with different values confirms the filtering logic works correctly.

  • If your Flask server is still running, it should auto-reload after you saved. If it is not running, start it by running:
python app.py
  • Open your browser and visit:
http://127.0.0.1:5000/api/alerts/low-stock
  • You should see a JSON response with a threshold of 10, a count of matching items, and an items array containing only items with a quantity of 10 or less.
  • Now test with a stricter threshold by visiting:
http://127.0.0.1:5000/api/alerts/low-stock?threshold=5
  • Confirm the count is lower than before and only items with a quantity of 5 or less appear.
  • Check that the items are sorted from lowest to highest quantity.

Getting an empty items array?

If the items array is empty, check that your Firestore documents have a field called quantity (exact spelling, lowercase). If your field is named differently (e.g. stock or qty), update the item.get("quantity", 0) references in your code to match your actual field name.

Also try a higher threshold (like ?threshold=100) to confirm data is coming through at all.

help me debug my low-stock alerts endpoint

✔️ Awesome, I've got everything!

Great. Double check you have saved app.py before moving on.

ⓧ I'd like to double check the full code

Here is the full app.py at this point. Compare your file against it:

from flask import Flask, jsonify, request
import firebase_admin
from firebase_admin import credentials, firestore

app = Flask(__name__)

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)
db = firestore.client()


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


@app.route("/api/inventory", methods=["GET"])
def get_inventory():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)
    return jsonify(items)


@app.route("/api/alerts/low-stock", methods=["GET"])
def low_stock_alerts():
    threshold = request.args.get("threshold", default=10, type=int)
    docs = db.collection("inventory").stream()
    alerts = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        if item.get("quantity", 0) <= threshold:
            alerts.append(item)
    alerts.sort(key=lambda x: x.get("quantity", 0))
    return jsonify({"threshold": threshold, "count": len(alerts), "items": alerts})


if __name__ == "__main__":
    app.run(debug=True)

Your API can now flag low-stock items on demand with a single URL call. Next up, you will build a CSV export endpoint that turns your inventory data into a downloadable spreadsheet file.

Build the CSV Export Endpoint

Your low-stock alerts endpoint filters and sorts data in Python. Now you'll take server-side processing a step further by generating a downloadable file.

Generating file downloads with proper HTTP headers is something a browser-side Firebase app cannot do on its own. Python's built-in csv module makes it straightforward to build an export endpoint that produces a proper CSV file for spreadsheet applications.

In this step, get ready to:
  • Import Python's csv and io modules for in-memory file generation.
  • Create a route that converts your inventory data into a downloadable CSV.
  • Test the file download in your browser.
Update your imports

The CSV export needs two Python standard library modules. csv handles converting dictionaries to CSV rows. io provides an in-memory text buffer so you can write the file without touching the disk.

  • In app.py, find the import section at the top of your file:
from flask import Flask, jsonify, request
import firebase_admin
from firebase_admin import credentials, firestore
  • Replace the entire import section with this updated version:
import csv
import io

from flask import Flask, jsonify, request, Response
import firebase_admin
from firebase_admin import credentials, firestore

What changed here?

  • csv and io are Python standard library modules. No extra pip install needed.
  • Response from Flask lets you set custom HTTP headers on the response, which is how you tell the browser "this is a file download."
Add the CSV export function

This endpoint fetches all your inventory documents, writes them into an in-memory CSV buffer, and returns the buffer as a downloadable file.

  • In app.py, find the if __name__ == "__main__": block at the bottom of the file. Add the following function above it:
@app.route("/api/export/inventory", methods=["GET"])
def export_inventory_csv():
    # Fetch all inventory documents from Firestore
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)

    # Return an error if no data exists
    if not items:
        return jsonify({"error": "No inventory data found"}), 404

    # Write CSV data to an in-memory buffer
    output = io.StringIO()
    fieldnames = sorted(items[0].keys())
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    for item in items:
        writer.writerow({k: item.get(k, "") for k in fieldnames})

    # Return the buffer as a downloadable CSV file
    return Response(
        output.getvalue(),
        mimetype="text/csv",
        headers={"Content-Disposition": "attachment; filename=inventory_export.csv"},
    )

What does this code do?

  • io.StringIO() creates a text buffer in memory that behaves like a file. You write to it without creating an actual file on disk.
  • csv.DictWriter takes a list of field names and writes each dictionary as a row, matching keys to columns automatically.
  • sorted(items[0].keys()) uses the first document's keys as column headers. Sorting them keeps the CSV columns in a consistent alphabetical order.
  • The Content-Disposition header tells the browser to treat the response as a file download named inventory_export.csv.
  • Save app.py.

Seeing a syntax error?

  • Make sure there is a blank line between the end of your low_stock_alerts function and the new @app.route decorator.
  • Check that the return Response(...) block is indented inside the function, not at the module level.

Help me fix a syntax error in my Flask CSV export function.

✔️ Awesome, I've got everything!

Great. Make sure you've saved the file before moving on to testing.

ⓧ I'd like to double check the full code

import csv
import io

from flask import Flask, jsonify, request, Response
import firebase_admin
from firebase_admin import credentials, firestore

app = Flask(__name__)

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)
db = firestore.client()


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


@app.route("/api/inventory", methods=["GET"])
def get_inventory():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)
    return jsonify(items)


@app.route("/api/alerts/low-stock", methods=["GET"])
def low_stock_alerts():
    threshold = request.args.get("threshold", default=10, type=int)
    docs = db.collection("inventory").stream()
    alerts = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        if item.get("quantity", 0) <= threshold:
            alerts.append(item)
    alerts.sort(key=lambda x: x.get("quantity", 0))
    return jsonify({"threshold": threshold, "count": len(alerts), "items": alerts})


@app.route("/api/export/inventory", methods=["GET"])
def export_inventory_csv():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)

    if not items:
        return jsonify({"error": "No inventory data found"}), 404

    output = io.StringIO()
    fieldnames = sorted(items[0].keys())
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    for item in items:
        writer.writerow({k: item.get(k, "") for k in fieldnames})

    return Response(
        output.getvalue(),
        mimetype="text/csv",
        headers={"Content-Disposition": "attachment; filename=inventory_export.csv"},
    )


if __name__ == "__main__":
    app.run(debug=True)
Test the CSV download
  • Restart the Flask server by pressing Ctrl+C in your terminal, then running:
python app.py
  • Open your browser and visit http://127.0.0.1:5000/api/export/inventory.
  • Your browser should either prompt a file download or display raw CSV text. Both confirm the endpoint is working.
  • If you see a downloaded file, open it in a spreadsheet application (Excel, Google Sheets, or Numbers). Verify that your Firestore inventory data appears in tabular format with column headers.

Why CSV instead of JSON?

Your /api/inventory endpoint already returns JSON. CSV serves a different audience. Business users can open a CSV directly in Excel or Google Sheets without writing code. This is the kind of server-side processing that justifies adding a Python backend to a Firebase app.

Getting a 404 error instead of a CSV?

  • A 404 with {"error": "No inventory data found"} means the endpoint is working, but your Firestore collection has no documents. Check that your inventory collection name matches your actual Firestore collection.
  • If the browser shows a generic 404 page (not the JSON error), the route itself is not registered. Double check that the function is decorated with the correct route path and restart the server.

Help me debug my CSV export endpoint.

You now have a working CSV export that generates downloadable reports from live Firestore data. Next up, you'll enable CORS and connect this API to your existing frontend so your inventory app can fetch alerts and trigger exports directly from the browser.

Add CORS and Connect Your Frontend

Your Flask API can serve inventory data, filter low-stock alerts, and generate CSV exports. Right now, you have been testing these endpoints directly in the browser address bar. That works because the browser and the server share the same origin.

Your existing frontend app lives on a different origin (a different port or domain). Browsers block cross-origin requests by default for security. In this step, you will enable CORS on your Flask API so your frontend can call it, then wire up a fetch call and a CSV download button.

In this step, get ready to:
  • Enable CORS on your Flask app so cross-origin requests are allowed.
  • Add a fetch call in your frontend to display low-stock alerts from the API.
  • Test the full integration between your frontend and the Flask backend.
Enable CORS on the Flask app

When your frontend (running on one port) tries to call your Flask API (running on port 5000), the browser blocks the request unless the server explicitly says cross-origin calls are allowed. The flask-cors package handles this with a single line of configuration.

  • In app.py, find this import line near the top of the file:
from flask import Flask, jsonify, request, Response
  • Add this new import line directly below it:
from flask_cors import CORS
  • Now find this line further down:
app = Flask(__name__)
  • Add this line directly below it:
CORS(app)

What does CORS(app) do?

CORS(app) tells Flask to include special headers in every response. These headers tell the browser that requests from any origin are allowed.

Without this, your frontend would receive a CORS error in the browser console and never see the response data. During development, allowing all origins is fine. In production, you would restrict this to your specific frontend domain.

  • Save app.py.
  • Restart your Flask server by pressing Ctrl+C in the terminal, then running:
python app.py
  • Visit http://127.0.0.1:5000/api/health in your browser to confirm the server is still running correctly.

Server won't start?

Check that the import line reads from flask_cors import CORS exactly. A typo like flask_Cors or flaskcors causes an ImportError.

Make sure CORS(app) appears after app = Flask(__name__), not before it. The app must exist before you can wrap it.

Help me fix my Flask CORS import error

Add a fetch call from your frontend JavaScript

Your Flask API is now accepting cross-origin requests. You can connect your existing inventory frontend to it using JavaScript's fetch API.

  • In your existing inventory app's JavaScript file, add a function that fetches low-stock alerts from the Flask API:
// Fetch low-stock alerts from the Flask API
fetch("http://127.0.0.1:5000/api/alerts/low-stock")
  .then(response => response.json())
  .then(data => {
    // data.items contains the low-stock inventory items
    // data.count tells you how many items are low
    // data.threshold shows the threshold used
    console.log("Low stock items:", data.items);
    console.log("Total alerts:", data.count);
  })
  .catch(error => {
    console.error("Error fetching alerts:", error);
  });

What does this code do?

fetch("http://127.0.0.1:5000/api/alerts/low-stock") sends a GET request to your Flask API. The browser receives the JSON response containing low-stock items.

The .then() chain first parses the response as JSON, then gives you access to the data. You can use data.items to render the low-stock list in your UI.

  • Connect the fetch results to your UI by replacing the console.log lines with code that displays each item in your app (for example, by creating list elements or updating a table).
  • For the CSV export, add a link or button in your HTML that opens the export endpoint in a new tab:
<!-- Add this button or link in your frontend HTML -->
<a href="http://127.0.0.1:5000/api/export/inventory" target="_blank">
  Download Inventory CSV
</a>

Why a link instead of fetch for the CSV?

The CSV endpoint returns a file with a Content-Disposition header that tells the browser to download it. Opening the URL in a new tab triggers the browser's native file download behavior. No JavaScript parsing needed.

  • Save your frontend files.
Test the full integration
  • Make sure your Flask server is still running in the terminal (you should see output at http://127.0.0.1:5000).
  • Open your frontend app in the browser (either from Firebase Hosting or locally via Live Server).
  • Trigger the low-stock check in your frontend (click the button or load the page where you added the fetch call).
  • Verify that your actual Firestore inventory data appears in the UI as low-stock alerts.
  • Click the CSV export button or link and verify that a file named inventory_export.csv downloads to your computer.

Seeing a CORS error in the console?

Open your browser's Developer Tools (**F12**) and check the Console tab. If you see a message about cross-origin requests being blocked, your Flask server may not have restarted after adding CORS(app). Stop the server with Ctrl+C and run python app.py again.

Also confirm the fetch URL in your JavaScript matches exactly: http://127.0.0.1:5000 (not localhost, which can behave differently in some browsers).

Help me debug my CORS issue between my frontend and Flask

No data showing up in your frontend?

Open the Developer Tools Console and look for error messages. If you see a network error, confirm the Flask server is running. If you see undefined values, check that you are accessing data.items (not data directly) since the response is wrapped in an object.

Help me figure out why my fetch response data isn't rendering

✔️ Awesome, I've got everything!

Great. Double check you have saved both app.py and your frontend files.

ⓧ I'd like to double check the full code

Here is the complete app.py file with CORS enabled:

import csv
import io

from flask import Flask, jsonify, request, Response
from flask_cors import CORS
import firebase_admin
from firebase_admin import credentials, firestore

app = Flask(__name__)
CORS(app)

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)
db = firestore.client()


@app.route("/api/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy"})


@app.route("/api/inventory", methods=["GET"])
def get_inventory():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)
    return jsonify(items)


@app.route("/api/alerts/low-stock", methods=["GET"])
def low_stock_alerts():
    threshold = request.args.get("threshold", default=10, type=int)
    docs = db.collection("inventory").stream()
    alerts = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        if item.get("quantity", 0) <= threshold:
            alerts.append(item)
    alerts.sort(key=lambda x: x.get("quantity", 0))
    return jsonify({"threshold": threshold, "count": len(alerts), "items": alerts})


@app.route("/api/export/inventory", methods=["GET"])
def export_inventory_csv():
    docs = db.collection("inventory").stream()
    items = []
    for doc in docs:
        item = doc.to_dict()
        item["id"] = doc.id
        items.append(item)

    if not items:
        return jsonify({"error": "No inventory data found"}), 404

    output = io.StringIO()
    fieldnames = sorted(items[0].keys())
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    for item in items:
        writer.writerow({k: item.get(k, "") for k in fieldnames})

    return Response(
        output.getvalue(),
        mimetype="text/csv",
        headers={"Content-Disposition": "attachment; filename=inventory_export.csv"},
    )


if __name__ == "__main__":
    app.run(debug=True)

Secret mission

Add Inventory Summary Analytics

Your API can list items and export them, but what about a bird's-eye view? Build an analytics endpoint that aggregates your entire inventory into totals, low-stock counts, and category breakdowns. This is the kind of computed insight a frontend alone cannot produce efficiently.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Your Firestore data already existed before this project and is not affected by cleanup.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Local inventory-reports project folder (contains virtual environment, app.py, requirements.txt, and serviceAccountKey.json).
  • Firebase service account key (registered in Google Cloud IAM).

Keep everything running

No action needed. Choose this if you are still actively building or plan to extend this API with more endpoints.

  • Your Flask server only runs when you start it manually, so it uses no resources when idle.
  • The serviceAccountKey.json file stays in your project folder. Keep it out of version control by adding it to your .gitignore if you push this project to GitHub.

Pause - I'll come back to this later

Shut down the Flask dev server to free up your terminal. Your files stay on disk for when you return.

  • If the Flask server is still running, press Ctrl+C in the terminal to stop it.
  • Deactivate your virtual environment by running:
deactivate
  • When you come back, reactivate the virtual environment with .venv\Scripts\activate and run python app.py to restart the server.

Delete - I don't want to use this again

Remove all project resources and revoke the service account key so it can no longer access your Firestore data.

  • If the Flask server is still running, press Ctrl+C in the terminal to stop it.
  • Deactivate your virtual environment by running:
deactivate
  • Delete the entire project folder by running:
cd ~/Desktop
rm -rf inventory-reports

On Windows, use:

cd ~\Desktop
rmdir /s /q inventory-reports
  • Revoke the service account key in the Google Cloud Console so it can no longer authenticate against your Firestore database.
  • Open the Google Cloud Console and go to the Service accounts page (under IAM & Admin).
  • Select your Firebase project.
  • Click the email address of the service account whose key you downloaded.
  • Click the Keys tab.
  • Click Delete next to the key you generated for this project.

Why revoke the key?

The serviceAccountKey.json file grants full admin access to your Firestore database. Deleting the local file removes it from your machine, but the key remains valid in Google Cloud until you explicitly delete it from the Keys tab. Revoking it ensures no one can use a copy of that file to access your data.

Nice Work!

Nice Work!

You just built a fully functional Flask REST API that connects to your live Firestore inventory data. This is the same pattern production teams use when they need server-side data processing alongside a Firebase frontend.

Here's what you accomplished:

  • Built a Flask REST API that reads live inventory data from your existing Firestore database using the Firebase Admin SDK.
  • Created a low-stock alerts endpoint with configurable thresholds and sorted results.
  • Built a CSV export endpoint that generates downloadable reports server-side using Python's csv module.
  • Configured CORS and connected the Flask API to your existing frontend using fetch.
  • Secret Mission: Added summary analytics with category breakdowns and aggregated inventory stats.

Ready to quiz yourself?