A poorly designed API can lead to hard-to-maintain code, frustrated clients, security risks, and performance bottlenecks. On the other hand, a well-designed API is predictable, consistent, scalable, and developer-friendly.
Here are five key considerations every developer should keep in mind when designing APIs.
1 - The Interface
At its core, an API is an interface between systems—a contract that defines how a client can interact with a service. This involves defining:
Resources: What are the core entities (e.g., users, products, orders)?
Endpoints: How can clients interact with those resources? (
GET /users
,POST /orders
, etc.)Request and Response Formats: Are you using JSON or XML? Are you following consistent naming conventions and data structures?
A good interface:
Follows clear naming conventions (nouns for resources, verbs for actions).
Uses predictable URI structures (e.g.,
/users/123/orders
).Provides useful and consistent HTTP status codes (e.g.,
200 OK
,201 Created
,404 Not Found
).
📌 Tip: Treat your API as a product. Even if internal, others will consume it, making their experience intuitive.
2 - API Paradigms
There’s no one-size-fits-all API style. The best choice depends on the use case, performance needs, and client flexibility.
Here are the most common paradigms:
REST (Representational State Transfer): Resource-based, stateless, widely used. Ideal for CRUD operations and simplicity.
GraphQL: Flexible query-based paradigm where clients define the shape of the response. Great for reducing over-fetching or under-fetching of data.
gRPC: High-performance, binary-based RPC protocol developed by Google. Ideal for internal service-to-service communication, especially in high-throughput systems.
Each has trade-offs:
REST is easy to understand and cacheable.
GraphQL is client-flexible but complex to secure.
gRPC is blazing fast but not browser-native.
Choose the paradigm that best aligns with your goals—don’t force everything into REST just because it’s common.
3 - Modeling Relationships
In most real-world systems, entities don’t exist in isolation. A user has multiple orders, a blog post has comments, a product has reviews.
Your API should make these relationships clear and accessible.
Example:
Instead of:
GET /orders?userId=123
Prefer:
GET /users/123/orders
This conveys the hierarchy and makes the relationship between resources explicit. It also improves discoverability and maintains semantic clarity in your endpoints.
For GraphQL, relationships are naturally expressed in the schema and resolved on demand:
{
user(id: "123") {
name
orders {
id
amount
}
}
}
Maintaining a clear, logical structure in how your API presents relationships improves both usability and maintainability.
4 - Versioning
APIs evolve. New features are added, bugs are fixed, data models change. But existing clients may still rely on older behaviors. That’s why versioning is essential.
There are a few common strategies:
URI versioning:
/v1/users
,/v2/users
Header versioning:
Accept: application/vnd.myapi.v2+json
Query param versioning:
/users?version=2
The goal is to:
Avoid breaking existing clients.
Communicate clearly that a change has occurred.
Allow parallel support for legacy and modern clients.
📌 Tip: Only bump a version when there’s a breaking change. Try to evolve APIs additively where possible.
5 - Rate Limiting
Without rate limiting, a single client could overload your system with thousands of requests per second, whether by accident or maliciously.
Rate limiting controls how many requests a client can make in a given time window (e.g., 100 requests per minute). This protects your backend and ensures fair usage across consumers.
Common techniques:
Fixed window: Simple count per time bucket.
Sliding window: Smoother traffic shaping.
Token bucket: Allows occasional bursts within a defined limit.
Rate limits are often communicated using headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 20
X-RateLimit-Reset: 1650000000
You can apply different limits based on:
API key or access token
User role (e.g., free vs. paid)
Endpoint type (e.g., heavy vs. light operations)
📌 Pro Tip: Always document your rate limits and include them in your API responses.
👉 So, which other key considerations do you keep while designing APIs?
Shoutout
Here are some interesting articles I’ve read recently:
The 10 Software Engineering Acronyms You MUST Know by
Every Outbox Needs an Inbox by
How A Single ZIP File Saved My Server From 1000s of Attacks (Code Inside) by
Hashing in Coding Interviews by
That’s it for today! ☀️
Enjoyed this issue of the newsletter?
Share with your friends and colleagues.
Rate-Limiting is essential if you don't want to be DDoSed.
Great article, Saurabh!
Thanks for the shoutout :)