21. Design a FastAPI service with an asynchronous database pool and retries.
Define endpoints and request and response contracts for a FastAPI service. Explain async database access, connection pooling, validation, timeouts, bounded retries, idempotency, error mapping, tracing, rate limits, and testing.
At a high level, I would expose POST /records and GET /records/{id} through a FastAPI service. The client sends an HTTPS request with a JWT through rate limiting and authentication. FastAPI validates the request, checks the Idempotency-Key when needed, and calls an async repository. The repository borrows a connection from the async pool and uses PostgreSQL. Temporary database failures use bounded retries with backoff. The trade-off is better reliability and connection reuse, but more state, monitoring, and failure-handling logic.
The goal is to serve record requests without creating one database connection per request. The main challenge is combining async access, safe retries, validation, and clear errors. I would explain the design by following the request from the client to PostgreSQL and back.
- Which clients and core use cases must the API support?
- What authentication, authorization, and data-validation rules should I assume?
- What scale, error handling, idempotency, and versioning requirements matter?
I would start with two endpoints. POST /records creates a record. GET /records/{id} reads one record using its identifier.
The client sends an HTTPS request containing a JWT and JSON data. HTTPS protects the request while it travels over the network. A valid JWT lets the edge layer authenticate the caller and inspect its claims.
The Rate Limit + Auth layer runs before FastAPI. It applies limits per IP address or user. It also validates the JWT and checks its scopes or claims. When the caller exceeds the limit, this layer returns HTTP 429 too many requests.
The accepted request reaches the FastAPI service. FastAPI uses Pydantic for request validation. Pydantic checks the expected JSON shape and field types.
When validation fails, FastAPI returns HTTP 422 validation error. Invalid data therefore never reaches the repository or PostgreSQL.
FastAPI also owns timeout handling, bounded retry policy, error mapping, and JSON response serialization. Keeping these rules inside the service gives both endpoints consistent behavior.
FastAPI checks or stores the Idempotency-Key in the Idempotency Store. The diagram shows Redis or DynamoDB as possible implementations.
Idempotency means a repeated request should not create duplicate work. This matters for POST /records because a client may retry after losing the first response.
The Idempotency Store supports request handling, but it is not the primary records database. PostgreSQL still owns the durable record and transaction data.
After validation, FastAPI sends a validated async call to the Async Repository / DB Access Layer. This layer builds queries, maps rows into domain results, and manages transactions.
The repository acquires a reusable connection from the Async Connection Pool. The pool controls minimum and maximum connection counts. It also performs health checks and connection recycling.
The pool sends the SQL query or transaction to PostgreSQL. PostgreSQL applies ACID transaction rules, indexes, and constraints. It returns rows or a commit result to the pool. The pool returns the database result to the repository. The repository then returns the domain result to FastAPI.
The design retries only timeout or transient database errors. A transient error is a temporary failure that may succeed later.
The retry policy performs retry 1..N with backoff on the repository and connection-pool path. Backoff means the service waits before another attempt. The retry count is bounded, so requests cannot retry forever.
If all attempts fail, FastAPI maps the failure to HTTP 503 / mapped error. This tells the client that the service is temporarily unavailable.
Retries improve recovery from short failures. However, they increase latency and may add database load during an incident.
After receiving the domain result, FastAPI serializes it as JSON. The service returns an HTTP 200/201 JSON response to the client. The read operation uses the successful read response, while creation uses the successful creation response.
FastAPI also sends trace spans, structured JSON logs, and metrics to the Tracing + Structured Logs component. The diagram includes OpenTelemetry traces and metrics for latency, errors, and retries.
This observability path helps operators debug failures. It does not own or delay the business response path.
The test suite uses pytest with HTTPX or TestClient. It tests endpoints, validation, timeout handling, and retry behavior.
Important cases include a successful record request, HTTP 422 for invalid input, and HTTP 429 for excessive traffic. Tests should also simulate transient database failures and confirm bounded retries. Repeated failures should end with HTTP 503.
These tests require controlled database and failure setup. The extra work is worthwhile because it protects the API contract and reliability rules.
The design adds several safety layers around a simple records API. Validation rejects bad JSON before database work begins. Rate limiting protects FastAPI and PostgreSQL from heavy callers. Idempotency protects repeated POST /records requests from duplicate work. The async connection pool improves throughput because requests reuse a controlled number of connections. Bounded retries help with temporary database errors, but they can increase latency and database load. Tracing and structured logs make failures easier to understand, but they add operational work. The benefit is better reliability and safer database use. The downside is more state, configuration, and testing. We accept this complexity because database connections are limited and client retries are common.
The interviewer is testing whether you can define a clear API boundary and trace the full request and response flow. They want correct judgment about async database access, connection pooling, validation, idempotency, rate limits, retries, and error mapping. They also check whether each component owns the right responsibility. A strong answer explains why retries must be bounded, why POST requests need idempotency, and what operational cost the design accepts.









