DEV Community

Cover image for Apollo-like hooks for REST APIs with React and Ketting
Evert Pot
Evert Pot

Posted on • Updated on • Originally published at evertpot.com

Apollo-like hooks for REST APIs with React and Ketting

A bit ago we released Ketting 6. This is the accumulation of about a year of learning on how to better integrate REST APIs with frontend frameworks, in particular React.

It's packed with new features such as local state management, new caching strategies, (client-side) middleware support and change events. It's also the first release that has some larger BC breaks to make this all work.

Releasing Ketting 6 is a big personal milestone for me, and I'm really excited to unleash it to the world and see what people do with. A big thank you to all the people that beta tested this in the last several months!

What's Ketting?

In short: Ketting is a generic REST client for Javascript. You can use it for pushing JSON objects via HTTP, but the richer your API is in terms of best practices and standard formats, the more it can automatically do for you.

It has support for Hypermedia formats such as HAL, Siren, Collection+JSON, JSON:API and can even understand and follow links from HTML.

It's often said that REST (and Hypermedia APIs) is lacking a good generic client. GraphQL has a lot of advantages, but a major one is tooling. Ketting aims to close that gap.

More information can be found on Github.

React support in Ketting 6

Ketting now has a separate react-ketting package that provides React bindings to Ketting. These bindings should look very familiar if you've used Apollo Client in the past.

Lets dive into an example:

Lets assume you have a REST api that has an 'article' endpoint. This returns something like:

{
  "title": "Hello world",
  "body": "..."
}
Enter fullscreen mode Exit fullscreen mode

This article is retrieved with GET, and updated with PUT, this is how you would display it:

import { useResource } from 'react-ketting';


export function Article() {

  const { loading, error, data } = useResource('https://api.example/article/5');

  if (loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div class="error">{error.message}</div>;
  }

  return <article>
    <h1>{data.title}</h1>
    <p>{data.body}</p>
  </article>;

}
Enter fullscreen mode Exit fullscreen mode

But what about mutations? Unlike GraphQL, mutations in REST APIs often have the same format for GET and PUT, so sending an updated resource to a server often just means mutating your 'data' and sending it back.

The following example would allow a user to edit an article and send it back:

import { useResource } from 'react-ketting';


export function Article() {

  const { loading, error, data, setData, submit } = 
    useResource('https://api.example/article/5');

  if (loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div class="error">{error.message}</div>;
  }

  const changeTitle = (title) => {
    data.title = title;
    setData(data);
  };
  const changeBody = (body) => {
    data.body = body;
    setData(data);
  };

  return <form onsubmit={() => submit}>
    <input type="text" value={data.title} onChange={ev => changeTitle(ev.target.value)  />
    <textarea onChange={ev => changeBody(ev.target.value)}>{data.body}</textarea>
    <button type="submit">Save!</button>
  </form>;

}
Enter fullscreen mode Exit fullscreen mode

Whenever setData is called, the internal Ketting cache is updated with the new resource state. This is globally stored, based on the uri of the resource.

This means that if multiple components use useResource on that same uri, every component will see that update and triggere a re-render.

This is similar to Apollo's local state, except it's still bound to a single resource uri and can eventually be saved.

When submit() is called, the data is re-serialized and sent in a PUT request.

Non-exhaustive list of other changes

  • Multiple cache strategies, such as forever, short and never.
  • Support for fetch-middlewares. OAuth2 is reimplemented as such a plugin. These plugins can be added globally, or per-origin.
  • get() now returns a State object, and functions such as put() will require one as an argument.
  • put() now automatically updates the state cache.
  • Support for HEAD requests and following links from HEAD response headers.
  • PKCE support for OAuth2.
  • Links can now be mutated and sent back to the server.
  • Nested transcluded items/embeds.
  • A separate post() and postFollow() method.
  • Better support for binary responses and text/* responses.
  • Experimental: Support for Siren actions, HAL forms and submitting HTML forms.

Future plans

The next two things we are working on are:

  • Support for more hooks/components for common React/REST API patterns (tell us what you want!).
  • Deeper support for forms and actions from HAL Forms, Siren and HTML.
  • Websocket support for live server-initiated state pushes.

More links

  • The documentation got a complete rewrite and is now hosted on the Github Wiki.
  • For a full list of changes and BC breaks, check out the Upgrading page.

Top comments (0)