Analogy of Hypertext-Driven RESTful APIs: Unleashing the Power of Hyperlinks

Analogy of Hypertext-Driven RESTful APIs: Unleashing the Power of Hyperlinks

Artistic architectural REST API designs: what to and what not to design.

When we think about developing RESTful APIs, one term that often comes to mind is HATEOAS (Hypermedia as the Engine of Application State). HATEOAS enables the API to guide the client’s navigation through the application by including hyperlinks in the API responses. While HATEOAS is a powerful concept, there is another approach to designing RESTful APIs that deserves attention: Hypertext-Driven APIs.

Understanding Hypertext-Driven APIs

Hypertext-Driven APIs, as the name suggests, leverage hypertext and hyperlinks to drive client interaction with the API. Unlike traditional APIs that require clients to have prior knowledge of resource URIs and actions, hypertext-driven APIs provide links within the API responses that allow clients to navigate and discover resources.

In this approach, the API acts as a sort of “hypermedia document” that not only provides data but also includes links to related resources and actions that can be performed. This empowers clients to dynamically explore the API without relying on hard-coded URLs or predefined navigation paths.

Now, before you continue reading, there are a couple of things you need to have installed to get you started on testing the codes on each example.

  1. Make sure you have Node.js & npm installed on your machine. You can download and install Node.js from the official website: https://nodejs.org or NPM from https://docs.npmjs.com/downloading-and-installing-node-js-and-npm

  2. Create a new directory for your project.

  3. Open a terminal or command prompt and navigate to the project directory.

  4. Copy the code example into a new file and save it with a .js extension, for example, ecommerce.js. In the terminal or command prompt, install the required dependencies by running the following command:

  5. npm install express

  6. Once the installation is complete, start the server by running the following command:

  7. node ecommerce.js

  8. You should see a message indicating that the server is running on a specific port (in this case, 3000).

  9. Open a web browser and navigate to http://localhost:3000/products to test the product listing endpoint.

    You should see a JSON response containing information about the available products.

  10. Try accessing other endpoints such as /cart, /cart/checkout, and /cart/payment_info to test different functionalities of the e-commerce platform.

    Feel free to explore the code and make modifications to suit your needs. You can add new routes, implement functionality for adding/removing items from the cart, or integrate with a database to persist data.

    Please note that this is a basic example for testing purposes. In a real-world scenario, you would need to consider additional security measures, handle database operations, and handle user authentication/authorization.

Rethinking API Interaction

  1. Imagine a RESTful API for an e-commerce platform. Traditionally, a client would need to know the specific URIs for retrieving a list of products, adding items to the shopping cart, and checking out. With a hypertext-driven API, the API response itself contains hyperlinks for these actions and more.

For example, instead of relying on a fixed URL for retrieving products, the API response may include a link with the relation type “products” that the client can follow to get the list of available products. Similarly, the response may include links to add items to the cart or proceed to the checkout.

Let's see some codes.

const express = require('express'); 
const app = express(); 
const PORT = 3000; 

app.get('/products', (req, res) => { 
const productData = [{ 
  id: '1', 
  name: 'Product 1', 
  description: 'This is product 1', 
  price: 10.99, 
  imageLink: 'https://via.placeholder.com/150', 
  addToCartLink: '/cart/add/1' }, 

{ 
  id: '2', 
  name: 'Product 2', 
  description: 'This is product 2', 
  price: 19.99, 
  imageLink: 'https://via.placeholder.com/150', 
  addToCartLink: '/cart/add/2' 
}]; 

  res.json(productData); 
}); 

app.get('/cart', (req, res) => {

const cartData = { 
  items: [ { 
  id: '1', 
  name: 'Product 1', 
  description: 'This is product 1', 
  price: 10.99, 
  quantity: 2, 
  imageLink: 'https://via.placeholder.com/150', 
  removeFromCartLink: '/cart/remove/1' } ], 
  subTotal: 21.98, 
  tax: 1.77, 
  total: 23.75, 
  checkoutLink: '/cart/checkout' 
}; 

res.json(cartData); 
}); 


app.get('/cart/checkout', (req, res) => {

const checkoutData = { 
  firstName: 'John', 
  lastName: 'Doe', 
  email: 'johndoe@example.com', 
  address: '123 Main St.',
  city: 'Anytown', 
  state: 'CA', 
  zipCode: '90210', 
  paymentInfoLink: '/cart/payment_info' 
}; 

res.json(checkoutData); 
}); 

app.get('/cart/payment_info', (req, res) => {

const paymentData = { 
  cardNumber: '**** **** **** 1234', 
  expDate: '06/25', cvv: '123', 
  paymentLink: '/cart/submit_payment' 
}; 
res.json(paymentData); 
}); 


app.post('/cart/submit_payment', (req, res) => { // handle payment submission 

res.sendStatus(200); }); 

app.listen(PORT, () => { 
console.log(`Server running on port ${PORT}`); 
});

In the above example, a GET request to /products returns information about available products, including hyperlinks for adding each product to the shopping cart. A GET request to /cart returns cart information, including items in the cart, a sub-total, tax, and a checkout hyperlink. A GET request to /cart/checkout returns checkout information, such as billing and shipping information, and a hyperlink for entering payment information. A GET request to /cart/payment_info returns a form for entering payment information, and a hyperlink for submitting payment information.

By breaking down the overall user flow into smaller resource interactions, each with hyperlinks to related resources, this e-commerce platform can be designed to be more flexible and adaptable, allowing clients to interact with the API more dynamically.

  1. Another example, Let’s consider an API for a social networking platform. A traditional RESTful API for this platform may have endpoints for retrieving users, posts, and comments, with specific URLs and routes for each of these resources.

However, a hypertext-driven API for this platform would include hyperlinks within the responses that allow clients to navigate and interact with the API in a more dynamic way.

For instance, a response for retrieving a user may not only include the user’s profile information but also hyperlinks to the user’s posts, followers, and the following list. Instead of requiring clients to know the URLs for these resources and making multiple API calls to retrieve them, clients can simply follow the hyperlinks to access additional information and resources.

Similarly, a response for retrieving a post may include hyperlinks to the post’s author, comments, and related posts. Clients can follow these links to explore the post’s context and engage with other relevant resources.

By including hyperlinks within the responses, a hypertext-driven API for a social networking platform can make the user experience more interactive and seamless. Clients can explore and interact with the API more naturally and intuitively, without having to rely on prior knowledge of specific URLs and APIs.

Get code this illustration:

const express = require('express'); 
const app = express();

const PORT = 3000; 

app.get('/users/:id', (req, res) => { 

  const userId = req.params.id; 

  const userData = { 
   id: userId, 
   name: 'John Doe', 
   bio: 'Software developer and music lover', 
   postsLink: `/users/${userId}/posts`, 
   followersLink: `/users/${userId}/followers`, 
   followingLink: `/users/${userId}/following` 
};
   res.json(userData); 
}); 


app.get('/users/:id/posts', (req, res) => { 

   const userId = req.params.id; 

   const postsData = [ 
{ 
   id: '1', 
   authorId: userId, 
   content: 'My first post', 
   commentsLink: `/posts/1/comments`, 
   relatedPostsLink: `/users/${userId}/related_posts` 
}, 
{ 
   id: '2', 
   authorId: userId, 
   content: 'My second post', 
   commentsLink: `/posts/2/comments`, 
   relatedPostsLink: `/users/${userId}/related_posts` 
  } 
]; 

res.json(postsData); }); // other endpoints for comments, following, related posts, etc. 


app.listen(PORT, () => { 
console.log(`Server running on port ${PORT}`); 
});

In the example above, a GET request to /users/:id returns information about a specific user, including hyperlinks for accessing their posts, followers, and following list. A GET request to /users/:id/ posts return the user’s posts, along with hyperlinks for accessing comments and related posts.

By including hyperlinks within the responses, clients can dynamically navigate and interact with the API without having to rely on hardcoded URLs or predefined navigation paths. This simplifies client development and promotes loose coupling between the client and server components.

Benefits of Hypertext-Driven APIs

  1. Improved Discoverability

Hypertext-driven APIs enhance discoverability by providing a roadmap for clients to explore available resources and actions. Clients no longer need to rely on API documentation or prior knowledge to navigate the API. They can simply follow links and let the API guide them.

  1. Flexibility and Adaptability

By using hyperlinks to drive interaction, hypertext-driven APIs become more flexible and adaptable. Adding or modifying resources and actions does not require changing client code, as clients can dynamically discover and interact with new features through the hyperlinks provided in the API responses.

  1. Loose Coupling

Hypertext-driven APIs promote loose coupling between the client and the API. The client does not need to have hardcoded knowledge of resource URLs or specific API routes. Instead, it relies on the hyperlinks provided by the API to navigate and interact. This decoupling makes the API more resilient to future changes and allows for easier evolution of both the client and server components.

  1. Simplified Client Development

Clients interacting with hypertext-driven APIs can focus more on business logic and user experience, as they can delegate the navigation and interaction aspects to the API itself. This can streamline client development, reduce complexity, and improve maintainability.

Conclusion

While HATEOAS is a well-known approach for designing RESTful APIs, hypertext-driven APIs offer an alternative perspective that emphasizes the power of hyperlinks. By embedding hyperlinks within the API responses, these APIs enable dynamic navigation and discovery, improving discoverability, flexibility, and the overall client experience.

Hypertext-driven APIs empower clients to rely on the guidance provided by the API, reducing the need for hardcoded URLs and predefined navigation paths. This promotes loose coupling, simplifies client development, and allows for easier evolution of both the client and server components.

So, next time you design a RESTful API, consider the potential of hypertext-driven APIs and embrace the power of hyperlinks. Let your API become a hypermedia document that guides clients on their journey and unlocks a whole new level of flexibility and adaptability.

Photo credit

Reference:

https://restfulapi.net/hateoas/

https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven