When we build APIs, we don’t just create connections—we shape the way data flows across systems. RESTful APIs, when designed well, act as long-lived, reusable assets that enable composability across the enterprise. But small missteps in design can lead to fragile implementations, broken integrations, and frustrated consumers. Good API design feels simple, clear, and predictable. Bad design feels confusing, brittle, and slow.
To build great APIs in MuleSoft using RAML, we must follow key REST principles. These principles guide how we name things, how we structure requests, and how we respond to users. Each principle has a purpose. When ignored, it causes problems to every system/application that depends on our API.In this post, we will explore each REST principle (or at least the ones that I follow), why it matters, and what happens when we don’t follow it.
1. Use Resource-Based URIs
REST treats everything—customers, accounts, payments—as resources. Each resource should have a unique name (URI). This makes the API feel like a map. You know where to go to find something. When we don’t follow this principle, URIs become cluttered and inconsistent. APIs become RPC-style, harder to navigate and maintain. For example, calling something like/getCustomerDetails?custId=1234
hides the purpose. It's harder to predict what the API will return. Developers guess instead of explore.Remember, REST is resource-oriented, not action-oriented. To follow this first principle we should:
Use nouns, not verbs
URIs should represent nouns (entities), not verbs (actions). Because of that, we should use nouns, not verbs to represent resources in URIs. URIs should describe what we access, not what we do.Some examples:
❌ Avoid | ✅ Prefer |
/getUserDetails | /users → collection of users |
/getCustomerAccounts?customerId=123 | /customers/123/accounts → list of accounts |
/createUser | /users/123 → specific user resource |
/deleteUserById | /accounts/987 → specific account |
Use plural nouns for collections
We should use plural nouns for collections (/users
, /transactions
).It’s also recommended to use camelCase or snake_case consistently for resources with 2 or more words, as well as for parameter names and JSON fields. For example creditCard, accountDetails
Show hierarchical relationships
Our API design needs to be intuitive. To make the consumer understand the relationship between the different resources in our API we should use nested URIs to show the hierarchical relationships between resources. For example:/customers/123/cases
/customers/123/transactions
If we follow these three recommendations, our resource-based URIs will be much more intuitive and clear for our API consumers. They will look like paths to objects and align very well with REST’s concept of resources, This makes APIs easier to read, test, and use. Also, it promotes uniformity and better caching support.
2. Use Standard HTTP Methods
REST uses HTTP verbs as actions. For that reason, RESTful APIs should map CRUD operations to HTTP methods appropriately. It’s simple and powerful. Each method has a clear meaning.For example:
CRUD Operation | HTTP Method | Example | Description |
---|---|---|---|
Create | POST | POST /users | Create a new user |
Read | GET | GET /users/123 | Retrieve a specific user |
Update | PUT / PATCH | PUT /users/123 | Update a user (PUT = full, PATCH = partial) |
Delete | DELETE | DELETE /users/123 | Delete a specific user |
We should avoid overloading GET/POST with other operations. For example, we should not use
GET /deleteUser?id=123.
Instead, we should use DELETE /users/123
This principle is important because the web understands these methods. Browsers, proxies, and tools behave as expected and it enables consistent expectations (e.g., POST creates, GET fetches).
When we don’t follow this principle, a
Stateless APIs scale well. They are easier to test and debug. However, when our APIs become stateful the API might store session data on the server. This means servers must remember past calls. Load balancing becomes hard. That adds memory pressure and makes it harder to balance traffic across servers. That’s why Statelessness is critical for horizontal scaling and high availability.
GET
that deletes data breaks caching and surprises users. Misuse creates dangerous and confusing APIs.3. Keep It Stateless
Each request must stand alone. It must carry all the information it needs—like authentication and parameters. The API should not remember any past request—no sessions. This improves scalability and reliability. For that, our requests should contain information like:- Authentication (e.g., Bearer token in
Authorization
header) - Content type (
Content-Type: application/json
) - Pagination info, filters, etc., in query parameters
Stateless APIs scale well. They are easier to test and debug. However, when our APIs become stateful the API might store session data on the server. This means servers must remember past calls. Load balancing becomes hard. That adds memory pressure and makes it harder to balance traffic across servers. That’s why Statelessness is critical for horizontal scaling and high availability.
4. Return the Right HTTP Status Codes
We must use the appropriate HTTP status codes to indicate the result of each operation. Status codes tell the client what happened, so this information should be consistent across all our APIs. A 401 or 404 error code must mean the same for all our APIs. If we follow this standard and make it consistent, we can then enable automated error handling and logging in our client applications. Some examples of our standard codes:Code | Meaning | Use case |
---|---|---|
200 | OK | Successful GET/PUT/DELETE |
201 | Created | Successful POST (resource created) |
204 | No Content | Successful DELETE or update with no response body |
400 | Bad Request | Invalid request from client |
401 | Unauthorized | Missing or invalid auth token |
403 | Forbidden | Valid token, but no permission |
404 | Not Found | Resource doesn’t exist |
409 | Conflict | Duplicate resource creation |
500 | Internal Server Error | Unhandled exception on server |
Have a look at this other blog post to know more about HTTP Status Codes - what they are and why they are important.
When we don’t follow this principle, errors become vague. Clients must guess what went wrong and Debugging becomes difficult; monitoring and alerting tools are less effective.
5. Use Standard Formats (MIME Types)
Our API must return data in common formats. We normally useapplication/json
as the default. JSON is easy to parse and it works well across platforms and languages. To enforce the use of JSON, we can support content types via headers like:Accept: application/json
Content-Type: application/json
6. Version the API in the URI
APIs evolve. Business needs change, data models shift, and integration logic improves. Without a clear strategy for versioning, even small changes can break downstream consumers. We should use versioning to offer a smooth transition between versions in our API endpointsFor that, we should use URI-based versioning (e.g.,
/v1/customers
) to make versioning explicit and visible. This approach is clear, cache-friendly, and widely adopted. Avoid implicit versioning via headers unless there’s a compelling reason. Some tips:- Always start with
v1
— never assume you’ll get it perfect the first time. - Treat major versions as new contracts: breaking changes warrant a new version.
- Support multiple versions in parallel during transitions, but deprecate with notice.
7. Support Pagination and Filtering
APIs that return large datasets without control mechanisms become a performance bottleneck — for both consumers and your own infrastructure. Mechanisms like pagination, sorting or filtering can really make the difference and make our APIs scalable and user-friendly.Filtering:
Use filtering parameters for specific fields. Examples:GET /accounts?type=savings&status=active
GET /transactions?status=failed&type=credit
Sorting:
Support sorting withsortBy=name&order=asc
.GET /transactions?sort=createdDate
Pagination:
Use standard query parameters for pagination likelimit
and offset
(or page
and pageSize
)GET /transactions?page=2&limit=50
{
"data": [...],
"pagination": {
"total": 215,
"limit": 20,
"offset": 0
}
}
8. Provide Clear Error Messages
When something goes wrong, developers need to know why. Vague or generic errors waste time and lead to fragile retry logic. Clear error responses help with debugging, support faster integration, and promote better API adoption.Use a standard error format with meaningful messages and HTTP status codes.to help consumers debug easily. For example, we could return a consistent error format, like this:
{
"error": {
"status": 400,
"code": "INVALID_PARAMETER",
"message": "The 'email' parameter must be a valid email address",
"details": [
{
"field": "email",
"issue": "Invalid format"
}
]
}
}
9. (Optional) Use Hyperlinks (HATEOAS)
HATEOAS is one of the defining constraints of REST, yet it's frequently omitted in enterprise API design. The principle aims to make APIs more discoverable, navigable, and self-descriptive — reducing client-side coupling and the need for external documentation.With HATEOAS, an API response not only returns data but also provides actionable links that tell the consumer what operations are available next. This enables clients to dynamically traverse resources based on their current state.
To put this into practice, we should Include hyperlinks in responses to guide clients through available actions:
{
"id": "12345",
"status": "active",
"type": "savings",
"balance": 2500,
"links": {
"self": {
"href": "/v1/accounts/12345"
},
"transactions": {
"href": "/v1/accounts/12345/transactions"
},
"close": {
"href": "/v1/accounts/12345/close",
"method": "POST"
}
}
}
Notice how we augment our API responses with contextual hypermedia links. Each link should include:
rel
: the relationship or intent (e.g.,self
,update
,delete
,next
)href
: the URL to perform the action- (optional)
method
: the HTTP verb (especially if it's not obvious)
We don’t need to follow this principle in all of our API resources/operations. It probably makes more sense in state transitions (e.g., submit, approve, cancel) or to guide consumers through workflow-driven APIs
When we don’t follow HATEOAS, our API clients will have to guess where to go next. Changes to URI structure break apps.
10. Secure Your API
Security is not optional. APIs expose our most critical business capabilities and data — often over public networks or shared environments. Without strong security controls, APIs become the weakest link, leading to data leaks, unauthorized access, or system compromise.In RESTful API design, security must be built-in, standardized, and enforced consistently across all layers and environments. Whether it’s for internal, partner, or public APIs, every endpoint must be treated as a potential attack surface.
- Use HTTPS for all traffic.
- Authenticate with OAuth 2.0, API keys, or JWTs.
- Enforce rate limiting, IP whitelisting, and input validation.