Skip to main content

Headless Drupal 8

Submitted by kaa4ever on Wed, 08/05/2015 - 08:15

Turning Drupal 8 into a headless monster.

The article is a part of an ongoing series about rebuilding thegoodwood.dk into a single page application with Drupal 8 and AngularJS. You can find the main article here.

Some basic configuration is required, to turn on the RESTful API for Drupal 8 . This includes activating the right core modules, updating the configuration synchronization, and setting up the right permissions. First I will show you how to do this, then I will show you how to use the API through GET (read), POST (create) and PATCH (update) requests.

Enable the core modules

Activate the three core modules; RESTful Web Services, Serialization & HAL. Or you can simply just activate HAL and the module dependency system will activate the rest for you.

Configuration synchronization

Drupal 8 introduces a new way of handling configuration. A lot of good articles already exist on this subject, and if you havn’t done so already, you should really read up on it.

Now that the modules are activated, we need to tell Drupal what we can and cannot do through the REST API. As the saying goes, there is a module for that. RESTUI provides this functionality through a neat UI, however when the site was in development, the module didn't work with the beta at that time.
But fear not, Configuration synchronization is here for the same.
Basically what we will do is import configurations that tell Drupal what entities are available for what operations (GET, POST, PATCH).

drupal8-configuration-synchronization

So go to Configuration > Configuration synchronization > Single Import/Export (admin/config/development/configuration/single/import), select ”Simple configuration” under Configuration type, and name it ”rest.settings” under Configuration name.
In the textarea below you input the following:

resources:
    'entity:node':
      GET:
        supported_formats:
          - hal_json
        supported_auth:
          - basic_auth
      POST:
        supported_formats:
          - hal_json
        supported_auth:
          - basic_auth
      PATCH:
        supported_formats:
          - hal_json
        supported_auth:
          - basic_auth
    'entity:taxonomy_term':
      GET:
        supported_formats:
          - hal_json
        supported_auth:
          - basic_auth
  langcode: en


So this is obviously settings written in YML.
What this tells Drupal is that entities of type node (entity:node) are available for GET, POST and PATCH operations, and what formats they will require.
Same goes for the taxonomy term entities (entity:taxonomy_term), but these are only available for GET requests.

One cool thing about Configuration synchronization is the way you are able to programatically access the configurations.
With the one line of code:

\Drupal::config(CONFIG_NAME)->get(KEY);

you can access what is stored in the configuration. So for instance:

\Drupal::config(‘rest.settings’)->get(‘resources’);

would return the settings defined above. Convenient and very powerful stuff!

Permissions

When you have specified which entities are available in the REST API, then the RESTful Web Services module will offer permissions for these entities.
So rebuild the cache and go to People > Permissions (admin/people/permissions) and scroll down to RESTful Web Services.

drupal8-permission-overview

You will find that you can now specify which roles are allowed to perform which operations.

Remember that granting permission for, by example anonymous users to “Access POST on Content resource” does not grant them access to create any nodes, only the access to the specific API endpoint.
So in the case where you would have users to create some kind of node, that same role must also have the “Node type: Create new content” permission.

Displaying content (GET)

Now with Drupal set up to serve content, you can now get a HAL representation of the content you have allowed.

In the setup part, we specified that nodes should be available in the hal_json format. This means that all you have to do, is request the URL of the content you want, with the right _format parameter.

drupal8-rest-get-content

and you get back that piece of content i HAL format. Easy right? See this if you wish to change the content of the response.

Creating content (POST)

To create content through the REST services you have to send a POST request to the endpoint of the entity type you wish to create. For nodes this would be /entity/node, which is the entity types I’ll show how to create in this example.

Building the content

As every Drupal developer knows, an entity consists of fields. Therefore to create a node through REST, we must build the structure of the node to create, and this structure must apply to the format we specified in the earlier steps of the setup. In this example it would be the HAL format.

If you are in doubt of how to format this data structur for some field types, you can always look at the response from a GET request, and build the data for the new node in the same manor.

There are only two required fields; Type and title. In plain JSON, the type is just a regular field definition, but in HAL the type must be a “link” to the specific entity type. The title is just a plain field definition. So to create a very shallow node with AngularJS, do like this:

// Build the data object.
var data = {
  "_links": {
    "type": {
      "href": "http://thegoodwooddk.dev/rest/type/node/page"
    }
  },
  "title": [{ "value": "My new content" }]
};
// Send the request.
$http({
  method: 'POST',
  url: '/entity/node',
  headers: {
    'Content-Type': 'application/hal+json',
  },
  data: data
});

If successful the POST will return a 201 header. In the header of the response you’ll find a Location key, which is the URL to the newly created entity. From this you can either extract the NID, or request this URL with a GET, to retrieve all metadata of the new node.

HAL, entity references and UUID

Drupal 8 introduces the concepts of UUID. It is very straight forward - every piece of content has a Universal Uniqie Identifier. When creating new content (or updating), this UUID is used as a reference. The reference must exist under an “_embedded” key. The following is an example of a data structure for creating a piece of content with two references:

var data = {
  "_links": {
    "type": {
      "href": "http://thegoodwooddk.dev/rest/type/node/order"
    }
  },
  "title": [{ "value": "My new content" }],
  "field_email": [{ "value": "[email protected]" }],
  "_embedded": {
    "http://thegoodwooddk.dev/rest/relation/node/order/field_products": [
      {
        "uuid": [{ "value": "c5a1985b-3da4-4def-8600-c9a82a56fca5" }]
      },
      {
        "uuid": [{ "value": "5d4dc29e-7c1b-40af-a100-f7f0c25b0f6d" }]
      }
    ]
  }
};

As you can see in this data snippet, the key for the reference is a link to the field, and the value is an array of references, defined by their UUID.
If you don’t know what the key (the link to the field) should be, try to GET content you know has a reference.

Updating content (PATCH)

It’s very easy to update content. Basically you only need to send a PATCH request to the URL of whatever content you want to update.
So if we were to update the email of the order created in the previous example, the data structure would look like this:

var data = {
  "_links": {
    "type": {
      "href": "http://thegoodwooddk.dev/rest/type/node/order"
    }
  },
  "field_email": [{ "value": "[email protected]" }]

};

Not that complicated right. Now to patch the content, create a http request like this:

$http({
  method: 'PATCH',
  url: '/node/126',
  headers: {
    'Content-Type': 'application/hal+json',
  },
  data: data
});

On success you get a 204 No Content status code, since the request really can’t or shouldn’t return anything. If you ask me, I don’t quite understand why we need to re- specify the content-type, but in times of writing this is required and can not be omitted.

Conclusion

It’s very easy to turn Drupal into a good RESTful backend. No need for contrib modules or anything, just a couple of clicks and you are ready to GET content however you want it. And that is powerful and what the web is really about.

Comment? Tweet me