Quality Assurance Labs
Web Development

Backend API & Microservices Architecture Guide

Senior Web Engineer9 min readPublished Updated

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.

Interconnected microservice server pods
#REST#GraphQL#microservices#API-design#backend-architecture

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

Notes from the lab.

Testing, engineering and growth — delivered to your inbox.

Need a backend architecture review? Book a call

Let's talk →