Skip to main content
In this lesson we finish wiring the fake-data tool so the FastAPI endpoint reads real rows from a local SQLite database (fakedata.db) instead of generating values with Faker. This walkthrough demonstrates how to:
  • Replace in-memory or stub logic with a repository-backed data source
  • Create a small DB helper for SQLite connections and row-to-dict conversion
  • Use parameterized queries (LIMIT ?) and avoid common pitfalls (bindings, imports, DB path)
  • Debug integration issues (module paths, missing tables, binding errors)
This is practical guidance for adapting an existing endpoint to a persistent data source while keeping the router thin and testable.

1) Original endpoint (Faker-based)

This was the original FastAPI endpoint that used the Faker library as a source of stubbed values:
Goal: replace the Faker generation with queries against an SQLite table so the endpoint returns real rows stored in fake_data.

2) Database table (reference)

Here is a sample table DDL used for the demo database:
Validate the table schema with:
This returns rows for the columns: first_name, last_name, email_address, age, city, occupation. Table summary: Tip: Use DB Browser for SQLite or the sqlite3 CLI to inspect the file and verify the fake_data table exists.

3) Add a DB connection helper

Create a tiny utility that opens the SQLite connection and sets row_factory so rows are easily converted to dictionaries for JSON responses:
Important: adjust './fakedata.db' to the correct path relative to your application process. Common mistakes are running the server from a different working directory or deploying with a different file layout.

4) Implement repository function to fetch rows

Add a repository function that executes a parameterized query and returns a list of dictionaries. Use LIMIT ? with parameters passed as a tuple (e.g., (count,)) to avoid binding errors and SQL injection risks:
Key points:
  • Use LIMIT ? and pass parameters as a tuple: (count,).
  • Convert sqlite3.Row to a dict before returning so FastAPI can JSON-serialize the response cleanly.

5) Update the router to call the repository

Replace the Faker-based business logic with a simple repository call. The router should be a thin adapter that validates the request, calls the repository, and returns the rows in a JSON response:
Note: ensure your import paths match the actual package layout. In this project the working solution used api.models.fake_data_request and data.fake_data_repository.

6) Request model (Pydantic)

Keep the Pydantic model small and focused. For the repository-driven endpoint you primarily need count. Optionally include locale if you plan to keep locale-aware behavior later:
If you later reintroduce per-field selection (e.g., data_type), extend this model and update repository logic accordingly.

7) Common errors encountered (and how to fix them)

When wiring DB-backed repositories into existing projects, these issues are common. The table below summarizes errors and remedies. Debugging tips:
  • Temporarily log the query and params to the application logs while debugging (remove or lower log level in production):
  • Confirm the Python process’ current working directory and the absolute path of the DB file:

8) Example client requests & responses

Request to retrieve 3 records:
Example successful response:
If the DB file is missing or the table doesn’t exist you will get a 500 with a detail message like:
This error is useful during development because it proves the endpoint is hitting the DB path instead of returning stubbed results.
Use tools like GitHub Copilot to accelerate repetitive tasks, but keep full control of architecture and imports. The LLM can suggest code and new files, but you must verify project structure, package imports, and the database path/permissions.

9) Summary / next steps

What we changed and next improvements:
  • Replaced Faker-based generation with a repository that reads rows from an SQLite fake_data table.
  • Added a DB connection helper that sets row_factory for easy dict conversion.
  • Implemented a repository function using parameterized queries and safe bindings.
  • Simplified the router to be a lightweight adapter that returns JSON-ready dicts.
  • Covered common integration errors: import paths, DB location, missing tables, and incorrect parameter bindings.
Next steps:
  • Add robust validation and error messages on the Pydantic model (e.g., ensure count is positive).
  • Add optional query filters and field selectors to the repository/API for more flexible responses.
  • Add unit and integration tests that exercise the database layer using a temporary test DB file or an in-memory SQLite DB.
  • Consider migrating to a connection pool (e.g., aiosqlite or a higher-level ORM) for production workloads that need concurrency.

Watch Video