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.
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 [email protected]: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
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.
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 tag before defining a Waterwheel global:
// On a browser not supporting ES6var 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.
// On a Node.js serverconst 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' ] */ });
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.waterwheel.getAvailableResources() .then(res => { /* [ 'comment', 'file', 'menu', 'node.article', 'node.page', 'node_type.content_type', 'query', 'taxonomy_term.tags', 'taxonomy_vocabulary', 'user' ] */ });
If this content did not answer your questions, try searching or contacting our support team for further assistance.
Fri Sep 12 2025 08:32:32 GMT+0000 (Coordinated Universal Time)