Backend API & Microservices Architecture Guide
APIs are the contract between frontend and backend. Design them like contracts — versioned, documented, authenticated, rate-limited, and monitored. Here's how we architect backend APIs at QA Labs.

Your frontend is only as good as the API it talks to. And your API is only as good as its architecture.
Backend API design is the discipline of building interfaces that are stable, versioned, documented, secure, and observable. Get it right and frontend teams move fast. Get it wrong and every release is a negotiation.
REST vs GraphQL vs tRPC
REST: Best for public APIs, simple CRUD, broad client support. Predictable, cacheable, universal.
GraphQL: Best for complex data graphs, multi-client products, evolving frontends. Powerful, but has learning curve and performance pitfalls (N+1, query complexity).
tRPC: Best for TypeScript monorepos, internal tools. End-to-end type safety, zero codegen.
Pick based on context, not hype. Most teams default to REST for public, GraphQL for complex internal, tRPC for TypeScript-internal.
API versioning
Version your API from day one. Even if you never release v2, the discipline prevents breaking changes.
Options:
URL versioning (/v1/users) — Simple, explicit
Header versioning (Accept: application/vnd.api.v2+json) — Clean URLs
Query parameter (/users?version=2) — Easiest, least clean
URL versioning is the most common. Stick with it.
Authentication and authorization
- API keys for server-to-server
- OAuth 2.0 + JWT for user-facing
- mTLS for high-security internal
- RBAC or ABAC for authorization
Never roll your own auth. Use battle-tested libraries.
Rate limiting
Every public API needs rate limiting. Common strategies:
- Per user (100 req/min)
- Per API key (1000 req/hour)
- Global (10,000 req/sec)
Return 429 Too Many Requests with Retry-After headers. Document limits publicly.
Microservices — when and when not
Microservices solve specific problems:
- Different scaling needs per service
- Different teams owning different domains
- Different languages for different services
- Independent deploy cadence
Microservices introduce:
- Network complexity
- Distributed tracing needs
- Distributed data consistency issues
- Operational overhead
Recommendation: Start with a monolith. Split into services when you have a concrete reason (not theoretical scale).
Observability
- Logs: Structured JSON, correlation IDs
- Metrics: Latency (P50/P95/P99), error rate, throughput
- Traces: Distributed tracing (OpenTelemetry)
- Alerts: Error rate thresholds, latency thresholds, saturation
You can't operate what you can't observe.
Documentation
Every API needs:
- OpenAPI spec (Swagger)
- Auth details
- Rate limits
- Error codes
- Example requests/responses
Generate docs from code where possible. Keep them current.
Common mistakes
- No versioning strategy
- Rolling your own auth
- No rate limits
- No observability
- Microservices too early
- Undocumented APIs
Key takeaways
- REST for public, GraphQL for complex, tRPC for TS-internal
- Version from day one
- Never roll your own auth
- Rate limit every public endpoint
- Start with a monolith; split when justified
- Observability is not optional
Further reading
About the author
Senior Web Engineer →Senior Web Engineer · Quality Assurance Labs



