The state of ReST in Headless Drupal 8

Drupal 8 ReST

One of the Drupal 8 initiatives was to make "headless" Drupal core work out of the box. Drupal now has a ReST API.

This allows you to use all the Drupal 8 features to edit your content but present the content not only on a Drupal frontend but also on mobile apps or anything else that can work with JSON data.

ReST

Drupal 8 has two modules that provide a ReST api. The rest module provides the simplest responses and can be used by requesting pages with the Accept: application/json header (AngularJS does this). The output of this api is just the result of the entity serializer in Drupal 8.

This is an example of a JSON response:

{
    "nid": [{
        "value": "1"
    }],
    "uuid": [{
        "value": "c0de5b48-d165-4970-8e05-5fccc692845f"
    }],
    "type": [{
        "target_id": "article"
    }],
    "langcode": [{
        "value": "und"
    }],
    "title": [{
        "value": "Rusticus Vulpes"
    }],
    "uid": [{
        "target_id": "0"
    }],
    "status": [{
        "value": "1"
    }],
    "body": [{
        "value": "This is the body of this node",
        "format": "plain_text",
        "summary": "And this is the summary"
    }],
    "comment": [{
        "status": "2",
        "cid": "5",
        "last_comment_timestamp": "1411546941",
        "last_comment_name": "",
        "last_comment_uid": "1",
        "comment_count": "5"
    }],
    "field_image": [{
        "target_id": "28",
        "display": null,
        "description": null,
        "alt": "Facilisi gemino nisl probo veniam.",
        "title": "",
        "width": "399",
        "height": "308"
    }],
    "field_tags": [{
        "target_id": "4"
    }]
}

HAL

When you enable the hal module and configur it you can request pages with the Accept: application/hal+json header. The HAL responses are the same as the normal JSON responses with an added _links and _embedded key that contains relation information about the requested entity.

This is an example of a hal+json response:

{
    "_links": {
        "self": {
            "href": "http:\/\/d8.dev\/node\/1"
        },
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/node\/article"
        },
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/uid": [{
            "href": "http:\/\/d8.dev\/user\/0",
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/revision_uid": [{
            "href": "http:\/\/d8.dev\/user\/0"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_image": [{
            "href": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg",
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_tags": [{
            "href": "http:\/\/d8.dev\/taxonomy\/term\/4",
            "lang": "und"
        }]
    },
    "uuid": [{
        "value": "c0de5b48-d165-4970-8e05-5fccc692845f"
    }],
    "type": [{
        "target_id": "article"
    }],
    "langcode": [{
        "value": "und"
    }],
    "title": [{
        "value": "Rusticus Vulpes",
        "lang": "und"
    }],
    "_embedded": {
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/uid": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/user\/0"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/user\/user"
                }
            },
            "uuid": [{
                "value": "aa69dd40-a5ad-4346-8d07-7c01f3e9e726"
            }],
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/revision_uid": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/user\/0"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/user\/user"
                }
            },
            "uuid": [{
                "value": "aa69dd40-a5ad-4346-8d07-7c01f3e9e726"
            }]
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_image": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/file\/file"
                }
            },
            "uuid": [{
                "value": "84c9914b-d5e1-4235-b911-17e0381948bd"
            }],
            "uri": [{
                "value": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg"
            }],
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_tags": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/taxonomy\/term\/4"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/taxonomy_term\/tags"
                }
            },
            "uuid": [{
                "value": "069bc39f-a41a-4fc6-a404-4df9e92e7b0b"
            }],
            "lang": "und"
        }]
    },
    "status": [{
        "value": "1",
        "lang": "und"
    }],
    "body": [{
        "value": "This is the body of this node",
        "format": "plain_text",
        "summary": "And this is the summary",
        "lang": "und"
    }],
    "comment": [{
        "status": "2",
        "cid": "5",
        "last_comment_timestamp": "1411546941",
        "last_comment_name": "",
        "last_comment_uid": "1",
        "comment_count": "5",
        "lang": "und"
    }]
}

A HAL response differs from a JSON response in replacing ids for relational data like image, user, tags and more by a entity in the _links key with a type, URI and a documentation link.

How to get to this point

Note: Most good demos need fixes described in rest meta issue

The easiest way to setup Drupal 8 for ReST development is using the drupal-rest-test tool. This tool uses drush to set up a Drupal installation, install the modules, configure the modules and set up the permissions. The project readme contains the information to set up the environment.

The missing pieces

Getting information from the hal or json api is pretty easy and the response is almost self-documenting. The big problems start when trying to add or modify nodes through the api. The documentation for this is non-existant and mostly reverse-engineered from the code. With this patch it's possible to POST new nodes to Drupal using the application/json format. This is implemented in the drupal-rest-test project in the post-rest.php file. The same thing using the application/hal+json format is implemented in the post-hal.php file.

And example for posting a node using application/json:

POST /entity/node
Content-type: application/json

{
    "title": [
        "test"
    ],
    "type": [
        {
            "value": "article"
        }
    ]
}

And a example for posting a node using application/hal+json:

POST /entity/node Content-type: application/hal+json

{
    "title": [
        "test"
    ],
    "type": [
        {
            "value": "article"
        }
    ],
    "_links": {
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/node\/article"
        }
    }
}

Creating entities with relations

One important thing in the API is creating entities that have relations to other entities. For example the Comment entity. This contains a relation to the Node entity.

Creating a comment using application/json (thanks @bertramakers):

POST /entity/comment
Content-type: application/json

{
    "entity_type": "node",
    "field_name": "comment",
    "entity_id": [
        {
            "target_id": 1
        }
    ],
    "comment_body": [
        {
            "value": "Example comment message."
        }
    ]
}

And doing the same using application/hal+json:

POST /entity/comment
Content-type: application/hal+json

{
    "entity_type": [
        "node"
    ],
    "field_name": [
        "comment"
    ],
    "comment_body": [
        "test"
    ],
    "_links": {
        "http:\/\/d8.dev\/rest\/relation\/comment\/comment\/entity_id": [
            {
                "href": "http:\/\/d8.dev\/node\/1"
            }
        ],
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/comment\/comment"
        }
    },
    "_embedded": {
        "http:\/\/d8.dev\/rest\/relation\/comment\/comment\/entity_id": [
            {
                "uuid": "af3710e7-fec3-4064-b500-b30d838236f5"
            }
        ]
    }
}

And yes: This is the minimum amount of data to create a comment using HAL

Building a frontend using ReST and Drupal

To test the API from an usability perspective we've started the drupal-8-rest-angular project. This projects tries to reimplement the Bartik theme as an AngularJS app. The project uses some custom views in Drupal 8 to get the required information from the API. Currently its build on the application/json format.

The project demonstrates everything that is missing from the API endpoints to recreate the complete Drupal frontend. There is no way to get the basic site information like the site name. Its impossible to get the content for the blocks and There are on average 5 json requests needed to present a single article to the user.

So if you're building a headless Drupal site or if you're doing something else with ReST. See the meta issue and report issues