Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 68

Designing RESTful APIs

Recommended resources

Hello! In our last lesson, we delved into Domain-Driven Design (DDD), establishing a powerful strategic framework for understanding a business domain and defining service boundaries called Bounded Contexts. We learned that these contexts are excellent candidates for becoming your microservices.

That leaves a critical question: once you have a well-defined service, how should it communicate its capabilities to the outside world? Today, we will explore one of the most prevalent and important architectural patterns for this: REST (Representational State Transfer). Your goal for this lesson is to learn how to design RESTful APIs following standard conventions and constraints, including HATEOAS. Mastering REST is essential for an architect, as it provides a standardized, stateless, and scalable way for services to interact.

1. The Path to REST: The Richardson Maturity Model

Before diving into specific rules, it's crucial to understand that REST is not a strict protocol like SOAP, but an architectural style defined by a set of constraints. Not all APIs that use HTTP are "RESTful."

To understand the journey toward a truly RESTful API, we can use a framework called the Richardson Maturity Model. It outlines four levels of maturity, providing a clear path from a basic HTTP-based API to one that fully embraces REST principles.

Richardson Maturity Model for REST APIs
This diagram illustrates the four levels of the Richardson Maturity Model, starting from Level 0 (basic RPC-style calls over HTTP) and progressing to Level 3, which incorporates Hypermedia Controls (HATEOAS) and represents the "Glory of REST."

To get a solid grasp of these levels, let's turn to a classic article by Martin Fowler that explains the model.

Richardson Maturity Model

The article 'Richardson Maturity Model' by Martin Fowler provides a foundational understanding of the different stages of API design, from simple remote procedure calls to a fully hypermedia-driven system.

Read the entire article, paying close attention to the examples for each level. As you read, notice how each level introduces a new constraint that brings the API closer to the REST ideal. Pay special attention to the jump from Level 2 to Level 3.

Let's briefly summarize the journey through the levels:

  • Level 0: The Swamp of POX (Plain Old XML/JSON): At this level, HTTP is used merely as a transport mechanism for Remote Procedure Calls (RPC). Typically, you have a single URI endpoint, and you send documents via POST to execute different procedures (e.g., POST /api { "action": "getUsers" }). This style doesn't leverage the capabilities of the web.

  • Level 1: Resources: This is the first major step. Instead of a single endpoint, you start thinking in terms of resources. Each resource gets a unique identifier (a URI). For example, you now have /doctors/123 and /appointments/456. This aligns well with your full-stack experience, where routing maps URLs to specific entities.

  • Level 2: HTTP Verbs and Status Codes: This level introduces the use of HTTP methods (verbs) to operate on the resources defined in Level 1. It also emphasizes using HTTP status codes to communicate the outcome of an operation. This is where many so-called "REST APIs" stop.

    • GET: Retrieve a resource (safe and idempotent).
    • POST: Create a new resource.
    • PUT / PATCH: Update an existing resource.
    • DELETE: Remove a resource.
    • Status Codes: Using 201 Created instead of 200 OK for a successful creation, or 404 Not Found for a missing resource.
  • Level 3: Hypermedia Controls (HATEOAS): This is the final and most mature level. HATEOAS stands for Hypermedia as the Engine of Application State. It means that the API response should include links that tell the client what actions they can perform next. This makes the API discoverable and decouples the client from hardcoded URIs.


2. Practical Conventions for RESTful API Design

Now that we have the conceptual framework of the RMM, let's focus on the practical conventions that make a Level 2 API clean, predictable, and easy for other developers to consume. Many of these practices are considered industry standards.

The following video provides an excellent, pragmatic overview of these conventions, drawn from real-world projects.

8 Pragmatic REST API Design Tips (From Real Projects)

The video '8 Pragmatic REST API Design Tips (From Real Projects)' by Milan Jovanović offers a concise and practical guide to common API design conventions.

Watch the segments covering the following tips: consistency (00:49), standard HTTP methods (01:32), correct status codes (02:42), plural nouns for resources (04:00), avoiding nested URLs (04:41), wrapping collection responses (05:41), and using a structured error format (08:41). The code examples are in .NET, but the principles are universal.

Let's consolidate the key design rules from the video and other best practices:

A. URI Design

Your URIs should be logical, predictable, and resource-oriented.

  • Use Nouns, Not Verbs: URIs represent resources (nouns), while HTTP methods represent actions (verbs).
    • Good: GET /users/123
    • Bad: GET /getUserById?id=123
  • Use Plural Nouns: Represent collections with plural nouns for consistency.
    • Good: /users (the collection) and /users/123 (a specific user from the collection).
    • Bad: /user and /user/123.
  • Avoid Deep Nesting: For relationships beyond one level, prefer using query parameters to filter.
    • Good: GET /users/123/posts (posts by a user).
    • Better than: GET /users/123/posts?status=published
    • Avoid: GET /users/123/categories/4/posts/5

B. HTTP Status Codes

Always return a status code that accurately reflects the outcome of the request. Never use 200 OK to report an error in the response body.

Mastering RESTful API Design: A Practical Guide

For a detailed list of common status codes and their meanings, this article, 'Mastering RESTful API Design: A Practical Guide,' provides a great reference.

Quickly review the sections 'Status Codes' and 'Server Responses'. Pay special attention to the 'Do Not Return a 200 Status Code for Errors' part, as this is a common anti-pattern.

C. Response and Error Formatting

  • Use JSON: Standardize on application/json for your responses.
  • Wrap Collections: Return arrays of resources inside a JSON object (e.g., { "data": [...] }). This allows you to add metadata, like pagination information, without breaking clients.
  • Provide Structured Errors: When an error occurs (e.g., 400 Bad Request), return a JSON object with a consistent structure that explains the error. The Problem Details standard (application/problem+json) is a great choice.
Test your understanding!

A junior developer has proposed the following API endpoints for a simple blog system. Refactor them to align with the REST conventions we've just discussed.

  1. POST /getallposts
  2. GET /post?id=42
  3. POST /createPost (with post content in the body)
  4. GET /deletepost?id=42
  5. GET /users/123/userposts
Show answer

Here is a refactored, more RESTful design:

  1. GET /posts: Uses the GET verb for retrieval and a plural noun posts for the resource collection.
  2. GET /posts/42: Uses a path parameter for the specific resource ID, which is standard practice.
  3. POST /posts: Uses the correct verb (POST) for creation on the collection URI.
  4. DELETE /posts/42: Uses the DELETE verb to indicate a destructive action on a specific resource.
  5. GET /users/123/posts: This is a good way to represent a nested resource. The original /userposts was unclear.

3. HATEOAS: The Engine of Application State

We've now covered what it takes to build a solid Level 2 API. To reach Level 3, the "Glory of REST," we must implement HATEOAS.

The core idea of HATEOAS is that a client interacting with a REST API should not need prior knowledge of all the specific URIs to navigate the application. Instead, the server provides links within its responses that guide the client to the next possible states or actions.

Imagine browsing a website. You don't type every URL manually; you click on links (<a> tags) that the server provides on the page. HATEOAS brings this same dynamic to APIs.

HATEOAS 101: Opinionated Introduction to a REST API Style - Webcast

The concept of HATEOAS can be abstract. This 'HATEOAS 101' video explains it clearly, using the analogy of a state machine and a direct quote from Roy Fielding, the creator of the REST architectural style.

Watch the sections that define HATEOAS within the REST constraints (01:56), explain the state machine analogy (05:36), and present Roy Fielding's definitive statement on the topic (23:18).

As Fielding states, an API that isn't driven by hypermedia is not truly a REST API. So, how do we implement this? We embed links in our responses.

A practical example is pagination. When you request a list of items, the API can include links for the next, previous, first, and last pages.

8 Pragmatic REST API Design Tips (From Real Projects)

Let's return to the '8 Pragmatic REST API Design Tips' video to see a concrete implementation of HATEOAS for pagination.

Watch the section on pagination and hypermedia links (06:44 to 08:51). Notice how the _links (or links) property is added to the enveloped response we discussed earlier.

Here's what a response with HATEOAS controls might look like:

{
  "data": [
    {
      "id": 123,
      "total": 50.00,
      "currency": "USD",
      "status": "shipped"
    }
    // ... more orders
  ],
  "links": {
    "self": { "href": "/orders/123" },
    "cancel": { "href": "/orders/123/cancel" },
    "track": { "href": "/orders/123/tracking" }
  }
}

With this response, the client application doesn't need to know how to construct the URL to cancel or track an order. It just needs to know to look for the cancel or track link relation (rel). This makes the system more resilient to change; the server can update the URI structure without breaking clients.


Conclusion

In this lesson, we journeyed from the foundational principles of REST to the practical conventions that define a well-designed API. You've learned to think about APIs not just as a collection of endpoints, but as a structured system of resources, operations, and discoverable state transitions.

Key Takeaways:

  • REST is an architectural style, and the Richardson Maturity Model (Resources -> Verbs -> Hypermedia) provides a framework for understanding its principles.
  • Well-designed APIs follow clear conventions: they use nouns for resources, plural URIs, correct HTTP verbs, and precise status codes.
  • Responses should be structured and predictable, using JSON, enveloping collections, and providing detailed error information.
  • HATEOAS (Level 3) is the key to a truly RESTful API. By embedding hypermedia links in responses, the server makes the API self-discoverable and decouples clients from specific URI structures.

Preview of the Next Lesson:
We now have a deep understanding of how to design a RESTful API. But is REST always the right choice? In our next lesson, we will compare the trade-offs of different API paradigms (REST, GraphQL, gRPC). This will equip you with the architectural knowledge to select the most appropriate API style for different use cases, a critical skill for any software architect.