In my previous blog post in this Waterwheel series, I detailed the basics of Waterwheel.js: how to set it up in server-side or client-side JavaScript, and how resource discovery can bring Drupal-backed applications and traditional Drupal implementations closer together in unprecedented ways. In this post, I explore how to manipulate content with Waterwheel.js.
Drupal 8’s REST API comes with CRUD (create, read, update, and delete) operations available on content entities (e.g. nodes, users, and taxonomy terms) by default, which means that Drupal-backed applications have a range of possibilities when it comes to interacting with content in a Drupal repository. In Drupal 8.2, resources available via REST also include configuration entities, which are now retrievable via GET queries.
Setting up a Drupal content store
Before you get started, you may wish to set up some content on Drupal. This section header is a bit of a misnomer, because setting up a Drupal content store is synonymous with installing a Drupal site and populating it with content. For simplicity’s sake, in this walkthrough, we’ll be using the Article and Basic page content types, as well as a custom content type called Product.
Setting up Drupal on your local machine is easy with Acquia Dev Desktop. First, clone the most up-to-date version of Drupal from GitHub and install Composer dependencies:
$ git clone [email protected]:drupal/drupal.git
$ cd drupal
$ composer install
Using Acquia Dev Desktop, click on the plus button and select “Import new local site.” Fill in the details; to ascertain the codebase location, select the “drupal” directory just created by the Git clone operation. Then, install Drupal normally by navigating to the domain you indicated. You can pay a visit to Acquia’s help center for more details about using Acquia Dev Desktop.
Once you have an installed site, you can generate some initial filler content. With Drush, a command-line tool for Drupal, you can very quickly generate nodes (drush genc
), taxonomy terms (drush gent
), and users (drush genu
). Visit the helpful resource Drush Commands for more information about Drush.
The series of commands below will create twenty nodes of various types, twenty taxonomy terms within the preexisting “Tags” vocabulary, and twenty users.
$ drush dl devel
$ drush en -y devel
$ drush en -y devel_generate
$ drush genc 20
$ drush gent tags 20
$ drush genu 20
Last but not least, if you have not enabled CORS (cross-origin resource sharing) support, you can make use of either core’s opt-in CORS support in 8.2 or Sally Young’s CORS module.
Reading and updating content entities: GET and PATCH
To perform a GET query, or read operation, on a node, you can simply use the method .get()
on the api object, as you can see below. All queries in Waterwheel return ES6 promises.
The method .get()
accepts two arguments, namely the identifier of the requested entity (required, e.g. nid
, tid
, uid
, etc.) and the format of the response (optional, defaults to JSON), which in an out-of-the-box REST API could be JSON or XML.
waterwheel.api.user.get(1)
.then(res => {
// Drupal JSON response
})
.catch(err => {
// Error
});
To request an entity in XML format, include the corresponding format argument:
waterwheel.api.user.get(1, ‘xml’)
.then(res => {
// Drupal JSON response
})
.catch(err => {
// Error
});
To perform a PATCH query, or update operation, against a content entity, you can use the .patch()
method, which includes an additional body argument containing a Drupal-compatible JSON object. This comes before the final format argument. The following query adjusts the title and body of the node of type Article having an nid of 1.
waterwheel.api.node.patch(1, {
"nid": [
{"value": "1"}
],
"type": [
{"target_id": "article"}
],
"title": [
{"value": "New title"}
],
"body": [
{"value": "New node"}
]
})
.then(res => {
// Updated entity in JSON
})
.catch(err => {
// Error
});
When the promise is fulfilled, this PATCH query will return the newly manipulated JSON object.
Adding and deleting content entities: POST and DELETE
To create a new entity, or perform a POST request, you can invoke the .post()
method in much the same way as an update operation. For instance, the following adds a node of type
Basic page with the title “Hello Drupal”.
waterwheel.api.node.patch({
"type": [
{"target_id": "page"}
],
"title": [
{"value": "Hello Drupal"}
],
"body": [
{"value": "How are you today?"}
]
})
.then(res => {
// 201 Created
})
.catch(err => {
// Error
});
Finally, to execute a DELETE query, you can simply provide the identifier argument and call it a day!
waterwheel.api.user.delete(2)
.then(res => {
// 204 No Content
})
.catch(err => {
// Error
});
Conclusion
As you can see, retrieving and manipulating Drupal content from the standpoint of Drupal-backed JavaScript applications is quite straightforward. By using Waterwheel.js’ built-in ES6 promises, you don’t need to worry about unwieldy AJAX requests, implementing your own XMLHttpRequests from scratch, or the specifics of core REST’s authentication mechanisms.
Moreover, Waterwheel.js allows JavaScript developers to worry about building their user interfaces and client-side code rather than grappling with the nuances of Drupal’s core REST API, such as query string parameters for correct formats and particular routes to reach the correct resource. Even if Drupal’s core REST API evolves in any of these characteristics, as an intermediate abstraction, Waterwheel.js’ API is well-positioned to remain optimal for JavaScript applications in the future.