Roger Mendoza

Why FastAPI and PostgreSQL are my default backend

For small products, dashboards and device back-ends, I keep reaching for the same boring, reliable stack. Here's the setup and why it holds up.

Whether it's an AI automation, an SEO platform or a place for ESP32 devices to post their readings, most of my projects need the same thing: a small, reliable API with a real database behind it. My default is FastAPI + PostgreSQL, deployed in a container. Here's why.

FastAPI: typed, fast to write, self-documenting

FastAPI builds on Python type hints. Declare what a request looks like, and you get validation, error messages and interactive OpenAPI docs for free:

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Reading(BaseModel):
    device_id: str
    temp_c: float = Field(ge=-40, le=125)
    battery_mv: int

@app.post("/readings", status_code=201)
async def add_reading(r: Reading):
    await save(r)
    return {"ok": True}

That model doubles as documentation for whoever writes the firmware on the other end. When an ESP32 sends malformed JSON, the API answers with a clear 422 instead of corrupting data.

Being Python also matters: the same language runs my automation scripts, data analysis, AI integrations and hardware test harnesses. One language across the stack means less context switching.

PostgreSQL: the database you don't outgrow

SQLite is wonderful for prototypes and on-device storage, and I use it there. For anything multi-user or long-lived, PostgreSQL earns its place:

  • Real constraints: foreign keys, unique indexes and check constraints keep bad data out at the source.
  • JSONB for semi-structured data — great for storing raw API payloads or AI outputs next to typed columns.
  • Full-text search that's good enough for many products without adding another service.
  • Mature backups, replication and tooling.

Boring on purpose

Startups and side projects die from complexity more often than from scale. This stack is intentionally boring:

  • One API service.
  • One database.
  • Background jobs in a simple worker or a scheduled task.
  • n8n or small scripts for glue work that doesn't belong in the core app.

When something breaks, there are only a few places to look.

Deployment

I package the API in Docker and deploy on platforms like Railway or AWS. A typical project has:

  • A Dockerfile and a health-check endpoint.
  • Migrations that run on deploy.
  • Environment variables for secrets — never committed to the repo.
  • Automated database backups, tested by actually restoring one.

That last point comes straight from my enterprise days: a backup you've never restored is a hope, not a backup.

When I'd choose differently

  • Heavy real-time features might push me toward something with first-class websockets and pub/sub infrastructure.
  • CPU-heavy number crunching might move into a separate worker or a compiled language.
  • Tiny static sites — like this one — don't need a backend at all.

The real reason

The best stack is the one that lets you spend your energy on the product instead of the plumbing. FastAPI and PostgreSQL get out of the way, and they still hold up when a weekend project turns into a real one.

Some links in these notes are affiliate links. If you buy through one, I may earn a commission at no extra cost to you. I only link to tools I use or would recommend.