API Design: REST, GraphQL, gRPC, and Strategic Choices
#api#architecture#rest#graphql#grpc
APIs are the contracts between systems. The choice of API style, versioning strategy, and governance model shapes how teams collaborate and how systems evolve. This is an architectural decision, not just a technical one.
API Styles Compared
| Aspect | REST | GraphQL | gRPC |
|---|---|---|---|
| Protocol | HTTP/1.1 or HTTP/2 | HTTP (single endpoint) | HTTP/2 |
| Data format | JSON (typically) | JSON | Protobuf (binary) |
| Schema | OpenAPI/Swagger (optional) | SDL (mandatory) | Proto files (mandatory) |
| Discoverability | HATEOAS, docs | Introspection built-in | Proto file sharing |
| Over-fetching | Common problem | Solved by design | Field masks (optional) |
| Under-fetching | Multiple round trips | Single query | Designed per RPC |
| Streaming | Limited (SSE, WebSocket) | Subscriptions | Bidirectional streaming |
| Browser support | Native | Native | Requires proxy (gRPC-web) |
| Caching | HTTP caching built-in | Complex (POST requests) | Custom implementation |
| Learning curve | Low | Medium | Medium-High |
When to Use What
- REST -- public APIs, simple CRUD, when HTTP caching matters, broad ecosystem compatibility
- GraphQL -- client-driven queries, mobile apps with bandwidth constraints, aggregating multiple backends
- gRPC -- internal service-to-service communication, high-performance requirements, streaming use cases
API Versioning Strategies
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL path | /api/v2/users | Simple, explicit | URL pollution, hard to sunset |
| Query parameter | /api/users?version=2 | Easy to default | Easy to miss |
| Header | Accept: application/vnd.api.v2+json | Clean URLs | Hidden, harder to test |
| Content negotiation | Accept: application/vnd.company.v2+json | RESTful | Complex implementation |
| No versioning (evolution) | Add fields, never remove | No version management | Requires strict discipline |
Recommended approach: URL path versioning for external APIs (clarity over purity), evolution-based for internal APIs (less overhead).
API Gateway Patterns
An API gateway sits between clients and backend services:
- Routing -- direct requests to the correct service
- Authentication -- validate tokens before requests reach backends
- Rate limiting -- protect backends from abuse
- Response transformation -- aggregate or reshape responses
- Caching -- reduce backend load for repeated queries
- Monitoring -- centralized logging and metrics
Popular options: Kong, AWS API Gateway, GCP API Gateway, Traefik, Envoy.
API-First Design
Design the API before writing implementation code:
- Define the contract (OpenAPI spec, GraphQL schema, or proto file)
- Generate mock servers for frontend teams to start immediately
- Generate client SDKs automatically
- Implement the backend against the contract
- Contract tests validate compliance
Benefits: parallel development, clear contracts, auto-generated documentation.
Internal vs External APIs
| Aspect | Internal APIs | External APIs |
|---|---|---|
| Audience | Your own services/teams | Third-party developers |
| Versioning | Can be more aggressive | Must be conservative |
| Breaking changes | Coordinate across teams | Deprecation periods required |
| Authentication | Service mesh, mTLS | API keys, OAuth 2.0 |
| Rate limiting | Trust-based, higher limits | Strict, tiered by plan |
| Documentation | May be informal | Must be comprehensive |
| SLA | Internal agreement | Contractual obligation |
Rate Limiting Strategies
- Fixed window -- N requests per time window (simple, but allows bursts at window boundaries)
- Sliding window -- smooths out the burst problem
- Token bucket -- allows controlled bursts while maintaining average rate
- Leaky bucket -- processes requests at a constant rate