DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • API Design First: AsyncAPI in .Net
  • RAML vs. OAS: Which Is the Best API Specification for Your Project?
  • From APIs to Actions: Rethinking Back-End Design for Agents
  • Translating OData Queries to MongoDB in Java With Jamolingo

Trending

  • The Hidden Bottlenecks That Break Microservices in Production
  • Visualizing Matrix Multiplication as a Linear Combination
  • Detecting Advanced Persistent Threats Using Behavioral Analytics and Log Correlation
  • Introduction to Retrieval Augmented Generation (RAG)
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Level Up Your API Design: 8 Principles for World-Class REST APIs

Level Up Your API Design: 8 Principles for World-Class REST APIs

Learn all about practical REST API design tips using the Richardson Maturity Model to build consistent, scalable, and easy-to-use APIs for developers.

By 
Akash Lomas user avatar
Akash Lomas
·
Akash Lomas user avatar
Akash Lomas
·
Nov. 28, 25 · Analysis
Likes (4)
Comment
Save
Tweet
Share
6.0K Views

Join the DZone community and get the full member experience.

Join For Free

You’ve probably built a “REST API” before. But what does “RESTful” truly mean? It’s not just about using JSON and HTTP. It’s a spectrum, best described by the Richardson Maturity Model (RMM).

  • Level 0 (The Swamp): Using HTTP as a transport system for remote procedure calls (RPC). Think of a single /api endpoint where all operations are POST requests.
  • Level 1 (Resources): Introducing the concept of resources. Instead of one endpoint, you have multiple URIs like /users and /orders.
  • Level 2 (HTTP Verbs): Using HTTP methods (GET, POST, PUT, DELETE) and status codes (2xx, 4xx) to operate on those resources. This is where most “REST” APIs live.
  • Level 3 (Hypermedia  —  HATEOAS): The “holy grail” of REST. The API’s responses include links (hypermedia) that tell the client what they can do next. The client navigates your API by discovering these links, not by hard-coding URLs.

The eight principles I’m sharing today are a blend of my own production experience and the pragmatic wisdom from industry-leading guides like Zalando’s. These should help you move your APIs up this maturity ladder, creating designs that are more robust, scalable, and easier to use.

1. Start With “API First” Design

Before you write code, design your API contract. Using a specification like OpenAPI (a.k.a Swagger) to define endpoints, request/response models, and error codes.

This “API First” approach forces you to think about the consumer’s experience. It serves as a living document for the team and allows front-end and back-end developers to work in parallel. It is the single most important step for ensuring consistency and building developer experience.

2. Use Nouns as Resources (RMM Level 1)

Your URLs should identify resources, not actions. The action should be defined by the HTTP method (see Tip 4).

  • Bad (Actions/Verbs):
    • POST /api/createUser
    • GET /api/getHabits
  • Good (Nouns/Resources):
    • POST /api/users
    • GET /api/habits

The best practice here is to use plural nouns for API collection resources. It is simple, consistent, and maps cleanly to the GET /habits/{id} pattern for retrieving a single item.

3. Keep Resource URLs Simple (Avoid Nesting)

Deeply nested URLs become unwieldy and hard to manage.

  • Deeply Nested (Avoid):
    • /users/{userId}/habits/{habitId}/entries

This URI is already complex, and it is easy to make it worse. What if you need all entries from a habit, regardless of the user?

A flatter design is more flexible. Instead, expose a top-level resource and use query parameters for filtering:

  • Flat (Prefer):
    • /entries?habitId={habitId}

It is not a strict rule, but if you find yourself nesting more than one level deep (/resource/{id}/sub-resource), it’s a red flag to reconsider your design.

4. Use HTTP Verbs Correctly (RMM Level 2)

The HTTP method is your verb. Let it do the work.

  • GET: Retrieve a resource or collection. (Safe and idempotent)
  • POST: Create a new resource. (Not idempotent)
  • PUT: Update a resource (replaces the entire resource). (Idempotent)
  • DELETE: Remove a resource. (Idempotent)
  • PATCH: Partially update a resource. (Often skipped for simplicity, but it’s the “correct” verb for partial updates).

Using these verbs correctly and consistently is the core of a Level 2 REST API.

5. Return Meaningful HTTP Status Codes (RMM Level 2)

Don’t just return 200 OK for everything. The status code is a critical part of the contract.

  • 2xx (Successful):
    • 200 OK: Standard success for GET.
    • 201 Created: The POST was successful, and a new resource was created. The response should include a Location header pointing to the new resource.
    • 204 No Content: The operation (like a DELETE) was successful, and there’s nothing to return to the body.
  • 4xx (Client Errors):
    • 400 Bad Request: A generic error for malformed input or a business logic failure (e.g., “Not enough stock”).
    • 401 Unauthorized: The user isn’t authenticated.
    • 403 Forbidden: The user is authenticated, but doesn’t have permission for this action.
    • 404 Not Found: The requested resource doesn’t exist.
  • 5xx (Server Errors):
    • 500 Internal Server Error: Your code crashed. The client can’t fix this.

6. Standardize Your Error Format

This is non-negotiable for a good API. When a 4xx or 5xx error occurs, your consumers shouldn’t have to guess the response format.

Stop inventing your own error objects. Use the industry standard: Problem Details (RFC 7807).

It’s a standardized JSON format (content type application/problem+json) that defines fields for:

  • Type: A URI pointing to documentation about this error.
  • Title: A short, human-readable summary.
  • Status: The HTTP status code.
  • Detail: A more specific explanation.
  • Instance: The URI of the resource that had the problem.

Almost all modern web frameworks have built-in support for Problem Details. Use it.

7. Use Envelopes, Pagination, and Hypermedia (RMM Level 3)

This tip combines three ideas that unlock the full power of your API, moving you toward RMM Level 3.

First, wrap your collection responses in an “envelope.”

  • Bad (Naked Array):
    JSON
     
     { 
       "id": 1, 
       "name": "Entry 1" 
     } 
  • Good:
    JSON
     
    {
    	"data": [
    		{ "id": 1, "name": "Entry 1" }
    	]
    }

8. Be Pragmatic and Consistent

Be consistent.

This is the most important rule:

  • If you use plural nouns, use them everywhere.
  • If you use 400 Bad Request for validation, use it everywhere for validation.

But also, be pragmatic. You may read that "true REST" (Level 3) is the only way. But Level 3 adds complexity for both the server and the client. Most of the world's best APIs live happily at Level 2, with some elements of Level 3 (like pagination links).

Use these principles. Scrutinize them for your context. And whatever you decide, apply it consistently.

An Example to Summarize

Let us walk through how this looks in a real application. This applies to any application, but for this case, we are assuming a "Habit Tracker" application. The example shows a RESTful API for managing personal habits, built on these principles.

EntriesController (an ASP.NET Core example), you can see:

  • API first (via attributes): I would define all response types ([ProducesResponseType]) and content types, which feed directly into the OpenAPI documentation.
  • Nouns and verbs: The route should be a plural noun [Route("entries")], and the methods are HTTP verbs like [HttpGet], [HttpPost] and so on.
  • Status codes: The POST endpoint returns 201 Created with the correct Location header. The GET (single) endpoint returns 404 Not Found or 200 OK.
  • Envelopes and pagination: The collection GET endpoint returns a PaginationResult object (the envelope) and includes logic to generate hypermedia links.
  • Error handling: The framework is configured to automatically return application/problem+json for all 4xx/5xx errors.

This practical, consistent application of these rules leads to an API that is predictable, scalable, and a pleasure for other developers to consume.

Conclusion

Good API design is not just about ticking boxes on the Richardson Maturity Model (RMM); it is about creating an easily understandable and consumable API for the developers who rely on your code. Whether you aim for the full discoverability of Level 3 hypermedia or settle into a robust, consistent Level 2 workflow, the ultimate metric of success is usability. 

Combining these structural best practices with a mindset of consistency and pragmatism will let you transform your API from a mere data pipe into a durable, intuitive product. Start small, stick to your contract, and build an interface that you would actually want to use yourself.

API Design REST

Opinions expressed by DZone contributors are their own.

Related

  • API Design First: AsyncAPI in .Net
  • RAML vs. OAS: Which Is the Best API Specification for Your Project?
  • From APIs to Actions: Rethinking Back-End Design for Agents
  • Translating OData Queries to MongoDB in Java With Jamolingo

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook