Skip to main content
In this lesson you’ll learn how to retrieve posts from a PostgreSQL database using psycopg2 inside a FastAPI application. We’ll cover a resilient connection pattern that retries while the database starts, how to execute a SELECT query, and how to fetch results correctly so FastAPI can return JSON responses.

1. Open a resilient database connection

Create a dedicated module (e.g., connection.py) that attempts to connect in a retry loop. This pattern is helpful when your database may still be initializing (for example in containers or managed services).
Make this module importable by your FastAPI app so route handlers can from connection import conn, cursor and execute queries.

Common connection errors and quick fixes

2. Understand cursor.execute() vs fetch methods

Important: cursor.execute() runs the SQL statement but returns None. To retrieve rows from a SELECT query you must call one of the fetch methods.
Remember: always invoke the fetch method with parentheses — e.g. cursor.fetchall() — otherwise you only have a reference to the function, not the query results.
Use the method that matches your expected result size: Always use parameterized queries to avoid SQL injection, passing parameters as the second argument to cursor.execute():
For write operations (INSERT, UPDATE, DELETE), call conn.commit() to persist changes, or set conn.autocommit = True.

3. Example in-memory posts (for comparison / simple create route)

4. Route handlers (main.py)

Below is an example FastAPI app that imports the database connection and demonstrates reading rows from PostgreSQL for the /posts endpoint. It also includes a simple in-memory create route to illustrate POST handling.

5. Example JSON response

Example response returned by the /posts endpoint (FastAPI serializes RealDictRow objects naturally as JSON):
Notes on types and formatting:
  • RealDictCursor yields RealDictRow objects that behave like dictionaries and serialize to JSON correctly.
  • PostgreSQL may add metadata columns such as created_at with timezone-aware timestamps.
Do not keep plaintext database credentials in source code for production. Use environment variables, configuration systems, or secret management (e.g., Vault, AWS Secrets Manager) and rotate credentials regularly.

6. Quick checklist and best practices

  • Use a retry loop for resilient connections when databases may be slow to start.
  • Call cursor.execute() to run SQL, then cursor.fetchall() / cursor.fetchone() / cursor.fetchmany() to retrieve results.
  • Use fetchall() for queries returning multiple rows and fetchone() for a single-row lookup by unique key.
  • For writes, call conn.commit() or enable conn.autocommit = True.
  • Always use parameterized queries to prevent SQL injection (e.g., cursor.execute(sql, params)).
  • Keep credentials out of code (use environment variables or secret stores).

Watch Video