/ Drupal

How to POST to a RESTfull endpoint in Drupal 8 with basic authentication

How to configure a REST endpoint in Drupal 8 and perform a POST (create a new node) via a HTTP POST request.

Be aware there's been important changes in how to configure Drupal 8 for REST from Before Drupal 8.2. Thus, be mindful for the tutorials you're reading and check the dates! This includes this article!

For example, regarding Permissions, you now only have to set permissions in one place. You used to have to grant separate permissions for rest endpoints however now in 8.2 and above you no longer need this since Drupal's REST automatically check access using the Entity Access API.

1. Enable Basic Authentication Provider Module & HAL

Enable the basic_auth and hal module both of which come with Drupal 8.

Basic auth is to require content creation from only authenticated users, and hal is a convenient (draft) standard for expressing relations between content.

HAL is a simple format that gives a consistent and easy way to hyperlink between resources in your API.

Enable basic_auth & hal module Using Drupal Console:
 vendor/bin/drupal moi basic_auth
 vendor/bin/drupal moi hal
Or via gui:

Drupal 8 enable HTTP Basic Authentication provider
Don't forget to enable the hal module!

This will have the effect of adding basic_auth: 0 and hal: 0 to your /config/sync/core.extension.yml file once you perform a config export.

If your working in a team using Drupal with git for version control, you'll want to commit these changes after performing a config export for each change.

2. Enable REST UI (optional)

To make it easy install and enable the REST UI module.

Drupal Console: Install and enable REST UI module
composer require drupal/restui
vendor/bin/drupal moi restui

The REST UI module avoids you having to manually create your config/sync/rest.resource.entity.node.yml file. We assume you're using the REST UI module, but have explain both ways below.

2. Set REST Settings For Content Resource

Go to REST UI settings via Home -> Administration:

Rest UI

Enable the 'Content' Resource:
Enable Content Nodes for Drupal REST

Since we're concerned with POST (creating new content via a POST request), enable the POST method, and GET for convenience.

For Accepted request formats choose hal_json as this allows us to express links between content, and set the content type which we wish to POST new content to.

For Authentication providers, choose basic_auth which we appears as an option due to enabling the module earlier.

Enable a content type for REST api post method in Drupal rest settings

We have enabled the 'basic_auth' as the authentication provider option since we've enabled the module; Drupal has a pluggable authentication provider system. The two two authentication providers Drupal 8 comes built in with are Basic Auth and Cookie.

3. Export your REST configuration (optional)

If you're working in a team with Drupal using git, you'll want to export this new configuration and merge it with your codebase.

Either using the gui or drupal console, export your site configuration. Use Drupal console or Drush rather than the ui, it will save you time and error.

Export Config Using Drupal Console:
vendor/bin/drupal config:export
Or via the gui:

Export Drupal Config Using the UI

Since you've enabled RETST on Content nodes, the drupal console config export will have created an additional config file:config/sync/rest.resource.entity.node.yml.

File rest.resource.entity.node.yml:

uuid: 678r44d5-4v5f-49ta-g333-g2cgd81d00d2
langcode: en
status: true
dependencies:
  module:
    - basic_auth
    - node
    - serialization
id: entity.node
plugin_id: 'entity:node'
granularity: resource
configuration:
  methods:
    - GET 
    - POST
  formats:
    - json
  authentication:
    - basic_auth

Your uuid will be different.

Note: You could have also created this file manually without the need for the Rest UI module, and placed it into your config/sync directory and imported the change. If you do this, don't add a uuid field, and the Drupal import generates this for you.

4. Create a user account for REST purposes

Create a user account in Drupal which you're going to use for the creation of these nodes. This user will be the one you set in the Authorization Basic header.

Create user for Drupal REST services

Here we create a user 'foo' and set the password to 'foo. Choose better credentials yourself!

If you don't set the right permissions for your user you will get the error:

"You are not authorized to create this node entity of bundle contact."

  • Give your user the correct permissions

4.1 Create a role for your user

It's important for security to limit the possible damage a rest user can do, so create a new role, for example "REST" and give the new user this role.

Create Rest Role for user

4.2 Give new user the 'REST' role

Giving user the REST role

4.3 Give the REST role the correct permissions

You may want to give this role permissions to create/read/update/delete (CRUD) all nodes, or (preferably) just the one you want to expose.

Edit the permissions of the REST role you've just created.

Edit permissions of the REST role

Here we just give the role the Contact: Create new content permission because this tutorial is purely about creating content via Drupal REST. Obviously if you wanted full CRUD access you'd enable more:

Giving REST user create node permissions
Give a little permissions as required for your use case.

5. Create Content Via REST POST Request

Understand that you can now create any content type (even your custom content types) via the rest API.

Because you've now configured rest on Drupal's entity.node type, and allowed POST requests with basic authentication you can restfully create any of your content types. This is because all content types you create inherit from the Node type in Drupal (see /core/modules/node/src/Entity).

In order for your REST POST request to be successful you need two things:

  1. Correct HTTP Headers set to:
    • Content-Type: application/hal+json
    • Authorization: Basic <Base64EncodedUser/Password>
    • X-CSRF-Token: abcxyz
  2. Valid payload (data) matching the field(s) of the content type you are creating

To use Drupal rest to create content types with custom fields you need to specify the node type (content type) in the body of your request with the _links.type.href attribute.

For example, suppose you have made a custom content type Contact which has 5 fields:

  • title
  • field_email
  • field_first_name
  • field_last_name
  • field_mobile

For example:
Customer Contact type in Drupal with first, last, email and mobile details

To build a POST request for this content type you must include the content type along with the request in a _links object in JSON.

Note the _links property for the Contact content type. For our Contact content type, the _links.type attribute would be: http://example.com/rest/node/contact.

The complete payload becomes:

{
    "_links": {
      "type": {
        "href": "http://localhost:8088/rest/type/node/contact"
      }
    },
    "title":[{"value":"Briggs"}],
    "field_email":[{"value":"briggs@example.com"}],
    "field_first_name":[{"value":"Ada"}],
    "field_last_name":[{"value":"Lovelace"}],
    "field_mobile":[{"value":"989"}]
}

The headers for the request would be:

Content-Type: application/hal+json
Authorization: Basic Zm9vOmJhcg==
X-CSRF-Token: 10iAHbWg0om4xjU1BcNFX2WDE5GC8pirOfR5Fprfrkk

Authorization Header: Is for the Basic Auth credentials, which are a base64 encoded string of your username:password.

X-CSRF-Token: Is a security mechanism to mitigate Cross Site Request Forgery (CSRF)
You get this token by visiting (manually or programmatically) example.com/session/token.

Note on security & securing Drupal Rest: Basic Authentication offers no dependable security unless you combine it with something like SSL. The authorisation header username and password are Base64 encoded, not encrypted. Anyone with access to your network would can decode the base64 string containing your user credentials. Take steps to protect these, by only permitting access to your endpoints over SSL, and give your 'rest' user account as least privileges as needed.

5.a Example Using Advaced Rest Client to Create New Node

Here we use Advanced Rest Client to create a new node of the 'Client' content type. We then do an example using python code.

Set correct headers

Setting correct heading for Drupal REST Post with Basic Authentication custom content type

Set correct body

Using Advanced Rest Client to add a new content type in Drupal Rest

If you had another content you wanted to POST to like (lets say, 'Car') then your _links.type.href attribute would be: http://example.com/rest/node/car, and update your fields accordinly.

5.b Drupal 8 REST create new node programmatically with custom fields

Here we use Python with the Requests library to create a new Contact via a POST request, passing all the required headers and fields:

example.py</small

import requests

#Get CSRF token
token = str(requests.get('http://127.0.0.1:8088/session/token').text)

endpoint = 'http://localhost:8088/node?_format=hal_json'

#Set all required headers
headers = {'Content-Type':'application/hal+json',
    'X-CSRF-Token':token
}

#Include all fields required by the content type
payload =  '''{
    "_links": {
      "type": {
        "href": "http://localhost:8088/rest/type/node/contact"
      }
    },
    "title":[{"value":"abc"}],
    "field_email":[{"value":"abc@example.com"}],
    "field_first_name":[{"value":"BDa"}],
    "field_last_name":[{"value":"gkl"}],
    "field_mobile":[{"value":"989"}]
    }'''

#Post the new node (a Contact) to the endpoint.
r = requests.post(endpoint, data=payload, headers=headers, auth=('foo','foo'))

#Check was a success 
if r.status_code == 201:
    print "Success"
else:
    print "Fail"
    print r.status_code

The python code above utilises our Drupal 8 RESTful endpoint to create a new entry of our custom content type Contact which has the custom fields first name, last name etc. The 201 response code means that the request succeeded and a new node has been created.

Note that the Authorization header is added in Python by passing an auth dictionary when we fire off the POST which is easier than manually encoding the authentication details as per Requests docs.

Congrats! You've successfully configured and created new content for your custom content type using Drupal's REST services module using JSON + HAL.

See also