← Back to Blog
Architecture

Building Scalable APIs with FastAPI and PostgreSQL

Step-by-step guide to a production-ready REST API — async handlers, connection pooling, JWT auth, and Docker.

GT
Genghix Tech
Backend Engineering Team
May 22, 2025
9 min read
Building Scalable APIs with FastAPI and PostgreSQL

Most of the backend work we ship at Genghix Tech runs on FastAPI backed by PostgreSQL. It's a combination that gives clients genuine production performance without the ceremony of heavier frameworks. This is the structure we default to when a new API project starts from a blank folder.

Why This Stack

FastAPI is built on Starlette and Pydantic, which means request validation, serialization, and automatic OpenAPI documentation come for free — not bolted on afterward. Because it's async-native, a single worker process can hold open hundreds of concurrent requests waiting on I/O (database calls, external APIs) without blocking. Paired with PostgreSQL's reliability and rich indexing options, it's a stack that scales from a weekend MVP to a system carrying real production traffic.

Project Structure

We keep the folder layout boring on purpose — boring is easy to onboard new developers into. A typical service looks like this:

app/
  main.py          # FastAPI app instance, router registration
  core/
    config.py      # environment-driven settings
    security.py    # JWT creation & verification
  db/
    session.py     # async SQLAlchemy engine & session
    models/        # ORM models
  api/
    v1/
      routes/      # one file per resource
  schemas/         # Pydantic request/response models
  services/        # business logic, kept out of route handlers
tests/
Dockerfile
docker-compose.yml

Async Handlers and Connection Pooling

Every route in a production service should be declared async def, and every database call should go through an async SQLAlchemy session backed by asyncpg. The database engine is created once at startup with a bounded connection pool, so the API never opens more connections than PostgreSQL is configured to accept.

  • pool_size and max_overflow are tuned to the number of worker processes, not left at defaults.
  • Sessions are scoped per-request via a dependency, and always closed in a finally block so a failed request can't leak a connection.
  • Slow queries get their own index review before they get thrown at a bigger database instance.

"The database is almost always the bottleneck before the framework is. We tune indexes and pool sizes long before we reach for a faster web layer."

JWT Authentication Done Properly

We issue short-lived access tokens and longer-lived refresh tokens, both signed with a rotating secret pulled from environment configuration rather than hardcoded. Passwords are hashed with bcrypt via passlib, and every protected route depends on a reusable get_current_user dependency rather than repeating auth logic per endpoint.

Rate limiting on the login and token-refresh endpoints is non-negotiable — it's one of the first things we add, since auth endpoints are the most common target for abuse.

Shipping with Docker

Every service ships with a multi-stage Dockerfile: a build stage that installs dependencies and compiles anything that needs compiling, and a slim runtime stage that copies only what's needed to run. A docker-compose.yml wires the API, PostgreSQL, and (when needed) Redis together for local development so a new developer can be running the full stack within minutes of cloning the repo.

Migration discipline

Every schema change goes through Alembic migrations, generated and reviewed, never applied by hand against production. This is the single habit that prevents the most painful class of production incidents.

Our Pre-Launch Checklist

  1. Connection pool sized and load-tested against expected concurrency.
  2. All protected routes covered by auth dependency tests.
  3. Structured logging and error tracking wired in before the first real user hits the API.
  4. Database backups and a tested restore procedure in place.

None of this is exotic — it's a disciplined, boring stack executed carefully, which is exactly what a production API should be.

FastAPI PostgreSQL Backend Docker
Share this article 💼 🐦

Ready to Build Your Next
Digital Product?

Let's talk. A free 30-minute strategy call could be the first step toward your best product yet.