REST (HATEOAS) and ReactJS
Asked Answered
K

2

19

I'm interested in using the HATEOAS principle of REST to reduce business logic in a SPA application. In a React-specific context, I'd like to know if there are challenges that make this impractical and, if not, what is a good strategy to follow?

Conceptual examples of using HATEOAS to remove business logic from the UI:

I've only found one link that suggests React/Flux is not compatible with a HATEOAS strategy, and no meaningful discussion elsewhere. Is it really not feasible in a React/Flux app? That SO post didn't get enough attention. Does anyone have a favorite or recommended approach for achieving success (with or without Flux or Redux)?

Someone gave a fairly detailed example of leveraging HATEOAS in the context of Angular. I'm looking for something similar for React.

Personally, I'm picturing the rel tag in hypermedia links controlling which JSX components are rendered (conditional JSX). Is that naive for a real-world React app? Perhaps conditionally rendered React components are too coarse-grained to be used this way?

I am assuming that hypermedia links are provided by a HAL implementation, or otherwise conform to the ATOM feed convention (RFC4287).

Kerato answered 5/3, 2016 at 20:55 Comment(0)
F
8

100% HATEOAS IS compatible with React & Flux, HATEOAS is compatible with Angular, HATEOAS is compatible with JQuery and even vanilla JS.

HATEOAS doesn't not impose any technical or implementation requirements on a consuming client.

HATEOAS is in fact simply a concept to which you can design your API (you can use one of several standards though like HAL)

Basically if you can call an API then you can implement a HATEOAS client.

So how to get there:

  • Step 1, how would you normally do an API call in React? Do it the same way.
  • Step 2, interrogate response.
  • Step 3, based on response, respond in the UI appropriately.

For example given a call to the order api /orders, I get the following response:

{
    "_links": {
        "self": { "href": "/orders" },
        "next": { "href": "/orders?page=2" }
    }
}

From this I can infer that next is a valid relation, and that if I go to that href I would in fact receive a second page of orders, so in this case in the UI show the next button.

However if I had received the following response:

{
    "_links": {
        "self": { "href": "/orders" },
    }
}

Then I could infer that next is not a valid relation, and in my UI I should disabled or not display a next button.

There is no magic, it is simply a change in thinking, a new paradigm.

Febrifugal answered 27/10, 2016 at 2:44 Comment(4)
it has some disadvantages the payload size will always be huge in case you intend to fetch some list with a larger data set as all of the records will be having _links stuff. It is a pain on mobile devices, your frontend will be very very chatty, and it really frustrates when you have to generate links to send back in some patch. besides you will often find yourself editing the links in _links to get the correct link. Also your app will have to paginate in every damn thing. You can only experience the pain once you actually do something with it in your react app.Accouter
So what happens with routing in a front-end application? If a user drills deep into an app via a URL, how does the front-end get the appropriate API link for the part of the app that is being loaded? After all, with HATEOAS you usually start with one link to the entry point of the API and follow links to get further into the app.Delozier
@RahilAhmad REST isn't meant for simple frontend 2 backend communication but for ecosystems where lots of clients, especially ones not under your control, have to interact with various servers/services and instead of tailoring to a particular API they couple to various media-types and provide mechanisms to operate on responses received for such. REST isn't meant to reduce the payload to a bare minimum but to support exploration and natural evolution for systems that last for decades. If REST doesn't fit your needs, don't use itBackcourt
@Delozier you basically need to manage a context of what your frontend already has explored thus far and what information it gathered while exploring the API. The link relations followed by a user basically will create a tree in your context where each branch in a tree more or less leads to its own dynamic page (or component). Certain media-types, i.e. hal-forms, might require you to render a form-component dynamically for the elements contained in the _template property of the response data. That context moreover should be able to invalidate certain branches in case the data is to oldBackcourt
Y
3

Before I spout off my most likely wrong/irrelevant answer, I just want to let you know that I just now read up on what HATEOAS is. That warning there, from what I've briefly read, HATEOAS seems to be mostly about the API telling you how to navigate through itself by providing you a link back to the resources that are relevant to the resource you had just requested.

That being the case, I don't see a reason why you can't implement dynamic url ajax calls in your actions that will alter your SPA's application state (i.e. Redux) based on what has been provided to you, however, you'll still need to have something to represent the state in a visual manner for all parts of your application. Here's a crude semi-pseudo and not very well thought-out representation of what I mean based loosely on your bank account example:

// our component file
import React from 'react'
import { makeAWithdrawl } from './actions'

export default React.createClass({
  handleClick: function(e) {
    e.preventDefault()
    makeAWithdrawl(this.props.account.withdraw.href)
  },
  render: function () {
    <div className="account">
      <p className="account_number">{this.props.account.accountNumber}</p>
      <p className="balance">{this.props.account.balance}</p>
      <p><a href={this.props.account.deposit.href}>Deposit</a></p>
      {this.props.account.withdraw ? <p><a onClick={this.handleClick}>Withdraw</a></p> : ''}
    </div>
  }
})


// our actions file
import store from 'store' // our redux store
import axios from 'axios' // an ajax library

export function makeAWithdrawl(url) {
  return axios.post(url).then(function(resp){
    store.dispatch({
      type: 'MAKE_WITHDRAWL',
      action: resp.data
    }) // do your reducer stuff
  })
}

Your application still knows what it's doing in the SPA, however, this will allow the API to direct you where to call to for whatever action needs to be performed. Hope it helps.

Yanina answered 6/8, 2016 at 3:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.