One of the Drupal 8 initiatives was to make "headless" Drupal core work out of the box. Drupal now has a ReST API.
>> 6 Modules cần tránh trước khi Drupal 8 Arrives
>> Phần 6 - New Back-End Features trong Drupal 8
>> Xem qua 1 số chức năng Drupal 8, Plugins, Guzzle, CMI, Caching
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