preston.so

Immersive Content and Usability is the first-ever book on immersive content and spatial content design, available now from A Book Apart.

Writing

Getting started with Waterwheel.js and resource discovery

September 30, 2016

Reprinted from the Acquia Developer Center with permission from DC Denison (Senior Editor, Acquia).

For developers of Drupal-backed JavaScript applications, simply having an HTTP client which can make requests against Drupal 8’s core REST API is often not enough. JavaScript developers are sometimes stymied by nuances unique to Drupal which leak out when consuming Drupal’s REST API, such as Drupal-specific paths for resources and confusing data structures in responses.

Waterwheel.js alleviates these barriers to JavaScript developers by providing an additional API atop Drupal REST which allows you to retrieve and manipulate content entities in Drupal 8 without needing to deeply understand Drupal’s internals. And thanks to the indefatigable efforts of Matt Grill, Waterwheel 1.0 has been released today as part of the DrupalCon Dublin contribution sprint!

In the first blog post in this series, Kyle Browning and I introduced the Waterwheel ecosystem, which is an emerging community surrounding SDKs which accelerate the development of Drupal-backed applications in JavaScript and Swift. In this blog post, I discuss some of the fundamentals of leveraging Waterwheel.js as an accelerator for your Drupal-backed JavaScript applications, whether they are progressively or fully decoupled.

Getting started with Waterwheel.js

To install Waterwheel.js, clone the GitHub repository using the following command. The master branch contains Waterwheel.js 0.8.1, which is the most up-to-date version. To use experimental features at your own risk, you can check out the 0.9.0 branch, which contains ongoing development work.

$ git clone git@github.com:acquia/waterwheel-js.git

To install dependencies which are required when invoking Waterwheel.js methods, you can use the following shorthand command to install packages such as

axios
and
qs
.

$ npm i

To build a browser version which you can include as part of a client-side JavaScript bundle, you can use the following command. This will create a file in the

dist
directory which contains all of Waterwheel.js’ functionality.

$ npm run build

Integrating Waterwheel.js with Drupal 8

If you are employing Waterwheel.js in a fully decoupled setting where the Drupal-backed application is on a domain different from that of Drupal, you will need to set up cross-origin resource sharing (CORS). In Drupal 8.2, opt-in CORS support is now available. This means that you can set up CORS support within Drupal rather than adding headers in your .htaccess file on Apache or Nginx.

In addition, the Waterwheel Drupal module is highly recommended for use with Waterwheel.js. Although it is not a strict dependency, some of the methods in Waterwheel.js will not work without the corresponding module (note that this is not true of Waterwheel.swift). The Waterwheel Drupal module is intended to track slightly ahead of core REST progress and bring certain features, such as resource discovery (see next blog post in this series), to Drupal sites before they are proposed or incorporated into core.

Instantiating Waterwheel

To instantiate Waterwheel.js on a Node.js server that includes ES6 support, use the

const
keyword in ES6 to require the Waterwheel module. Thereafter, you can instantiate a new Waterwheel object by providing it a base Drupal site URL and an OAuth2 token.

// On a Node.js server 
const Waterwheel = require(‘waterwheel’); const waterwheel = new 
Waterwheel({base: 'http://drupal.dd, credentials: {oauth: '12345'}});

If you want to make Waterwheel available on your browser with ES6 support, you can import the generated bundle (see above) and attach it to the

window
object:

// On a browser supporting ES6 import 
'../../path/to/node_modules/waterwheel/dist/waterwheel.js'; const 
waterwheel = new window.Waterwheel({base: 'http://drupal.dev', 
credentials: {oauth: '12345'}});

If your browser doesn’t support ES6, you can include the asset in a traditional

<script>
tag before defining a Waterwheel global:

// On a browser not supporting ES6
var waterwheel = new window.Waterwheel({base: 'http://drupal.dev', credentials: {oauth: '12345'}});

The instantiation of Waterwheel also supports a property consisting of resources which can be supplied to your Waterwheel-driven application. See “Populating and accessing resources in Waterwheel.js” below for more information. In addition, you can provide a

timeout
argument, which defines for Waterwheel how long an HTTP request should be permitted to idle before being cancelled.

Resource discovery

The Waterwheel Drupal module introduces a new feature known as resource discovery, which enables client-side applications to understand the Drupal content schema without relying on responses from the server. After all, one of Drupal’s key features is the customizability of the content model, meaning that new content types and corresponding fields can be added arbitrarily.

Client-side applications are unaware of the structure of these content types and fields, and without an understanding of how these entity resources are serialized, front-end developers are ill-equipped to include features such as client-side validation against a Drupal content schema and client-specific additions to the content model which reside alongside what exists in Drupal.

Populating resources in Waterwheel.js

As mentioned above, if you wish to supply these resources dynamically in order to develop Waterwheel-driven applications in the complete absence of a Drupal site, you can pass resources in as an additional argument upon instantiating Waterwheel. This can, for instance, be provisioned by requiring an additional JSON file.

// On a Node.js server
const Waterwheel = require(‘waterwheel’);
const waterwheel = new Waterwheel({base: 'http://test.dev', credentials:
 {oauth: '12345'}, resources: require('./resources.json')});

If you do not provide an additional

resources
argument upon instantiating Waterwheel, you can populate resources for Waterwheel through an additional API call to a resource provided by the Waterwheel Drupal module. This is something available to you in the
populateResources()
method, which can be invoked as seen below using ES6 promises:

waterwheel.populateResources()
.then(res => {
/*
[ 'comment',
'file',
'menu',
'node.article',
'node.page',
'node_type.content_type',
'query',
'taxonomy_term.tags',
'taxonomy_vocabulary',
'user' ]
*/
});

Manually adding new resources

You can also manually add resources to Waterwheel. This could be useful if you wish to have other entity structures alongside your Drupal entities, such as from another external content store. In this case, the parent object key will be used as the new resource’s name, and you can add an arbitrary number of custom resources as additional arguments:

waterwheel.addResources(
{myNewResource: {
base: {{ base url }},
credentials: {{ credentials }},
methods: {{ methods }},
entity: 'node',
bundle: 'page',
resourceInfo: {{ extended information path }}
 }}
);

The properties of the resources consist of the following, as quoted from the README:

  • base: 
    The base path for your Drupal instance. All requests for this resource will use this path. This can be different from the path used when instantiating
    waterwheel
    .

  • credentials: 
    An object containing the username and password used to authenticate with Drupal. This can be different from the credentials used when instantiating waterwheel.

  • methods: 
    An object containing the following keys:
    GET
    ,
    POST
    ,
    PATCH
    ,
    DELETE
    . Each key should contain the path suffix that the action can be performed on.

  • entity: 
    The entity type that this resource should reference, i.e.
    node
    .

  • bundle: 
    The bundle that this resource should reference, i.e.
    page
    .

  • options: 
    The path used to get extended (field) information about the bundle. This is usually provided automatically by the Waterwheel Drupal module but can be manually specified.

Once you have populated Waterwheel’s available resources, you can access them using the

getAvailableResources()
method:

waterwheel.getAvailableResources()
.then(res => {
/*
[ 'comment',
'file',
'menu',
'node.article',
'node.page',
'node_type.content_type',
'query',
'taxonomy_term.tags',
'taxonomy_vocabulary',
'user' ]
*/
});

Conclusion: Resource discovery in core?

Whereas Waterwheel.js is intended to accelerate the development of Drupal-backed JavaScript applications, our mission is also to provide the underpinnings of greater closeness between Drupal and the JavaScript applications it powers as a corollary to the API-first initiative in Drupal. By providing the means for resource discovery, wherein JavaScript applications are intimately aware of Drupal’s content model on the client side, doors of opportunities are opened in the areas of client-side validation and client-side extensions to the schema.

Currently, this functionality requires the Waterwheel contributed module in Drupal, which is intended to track slightly ahead of Drupal 8 core in a visionary fashion for tooling that will advance Drupal as its use as a repository for JavaScript applications grows. However, our long-term intent is to remove this dependency by providing for resource discovery in core.

We need your help and feedback! Contributions to Waterwheel.js are always welcome, and your thoughts on resource discovery and other features in the library would be greatly appreciated. In the next blog post in this series, we’ll cover how you can query and manipulate content through Waterwheel.js to take your Drupal-backed JavaScript application to the next level.

Special thanks to Matt Grill for feedback during the writing of this blog post.

Before you go ...

Subscribe to my occasional newsletter. No spam. Just resources and exclusive ideas about omnichannel content and more. Also, be the first to know when my book Immersive Content and Usability is launched.

Trusted by

  • Genero
  • Srijan
  • Tag1 Consulting