ANNAIマガジン
Node.jsを用いて、REST APIで外部からDrupal8にコンテンツを投稿する
この記事の目次

HeadlessにDrupalを扱うというテーマはここ最近よく耳にするようになりましたね。そこで今回はDrupalに対して外部からREST API経由でコンテンツを追加する方法について紹介します。

参考記事

RESTful Web Services module - 3. POST for creating content entities

https://www.drupal.org/docs/8/core/modules/rest/3-post-for-creating-content-entities

事前準備

Drupalで管理画面からREST APIを設定するには、REST UIモジュールが必要です。

REST UI: https://www.drupal.org/project/restui

またRESTリソースへのアクセス制御を事前に設定する必要がありますので、更新したいコンテンツに対するPOSTを許可するようにしましょう。なお、今回はPOSTすることに主題を置くため、認証はBasic認証を利用します。実際にサービスへ投入する際などはSimple OAuthモジュール(https://www.drupal.org/project/simple_oauth)などでOAuthを設定することをオススメします。

RESTリソース

Node.jsからAPIを呼び出す処理を書く

HTTPリクエストを飛ばすためのモジュールをインストールします。

$ npm init -y
$ npm i -S request request-promise btoa

index.jsファイルを作成して、以下のようにコードを用意します。

const request = require('request-promise')
const btoa = require('btoa')
const endpoint = 'https://YOUR_DRUPAL.COM
const username = 'admin'
const password = 'admin'
const format = 'hal_json'

/**
* DrupalにPOSTするObject
*/
const newNode = {
 _links: {
   type: {
     href: endpoint + '/rest/type/node/article'
   }
 },
 type: {
   target_id: 'article'
 },
 title: {
   value: 'Hello REST API'
 },
 body: {
   value: `This is example post.<br /><h1>HTML content</h1>`
 },
 moderation_state: [ { value: 'published' } ]
};

/**
* Client Object
*/
class RESTClient {
 /**
  *
  * @param {string} endpoint API endpoint
  * @param {string} [type='hal_json'] Request type
  */
 constructor (endpoint, type = 'hal_json') {
   this.endpoint = endpoint
   this.authorization = ''
   this.contentType = type
 }
 /**
  * get content type string
  * @param {string} place called place
  * @return {string}
  */
 getContentType (place) {
   if (!place) return this.contentType
   if (place === 'header') {
     if (this.contentType === 'hal_json') return 'application/hal+json'
     if (this.contentType === 'json') return 'application/json'
   } else if (place === 'path') {
     if (this.contentType === 'hal_json') return '?_format=hal_json'
     if (this.contentType === 'json') return '?_format=json'
   }
   return this.contentType
 }
 /**
  * get CSRF token
  * @return {Promise<string>}
  */
 async getCsrfToken () {
   const requestParam = await this.getRequestParam('GET', 'rest/session/token')
   const { body } = await this.sendRequest(requestParam)
   return body
 }
 /**
  * create authorization by basic auth
  * @param {string} username Drupal admin username
  * @param {string} password Drupal admin password
  */
 configureBasicAuth(username, password) {
   const basicAuthCredential = username + ":" + password;
   const bace64 =  btoa(basicAuthCredential);
   this.authorization = `Basic ${bace64}`;
 }
 /**
  * get authorization
  * @return {string} authorization header string
  */
 getAuthorization () {
   return this.authorization
 }
 /**
  * get request header
  * @return {Promise<Object>} header object
  */
 async getHeader() {
   if (!this.authorization) throw new Error('authorization is required')
   const csrfToken = await this.getCsrfToken()
   if (!csrfToken) throw new Error('X-CSRF-Token is required')
   return {
     'Content-Type': this.getContentType('header'),
     'Authorization': this.getAuthorization(),
     'X-CSRF-Token': csrfToken
   }
 }
 /**
  * Get request object
  * @param {string} method HTTP method
  * @param {string} path request path
  * @param {object} [body={}] request body
  * @return {Promise<{}>}
  */
 async getRequestParam (method, path, body = {}) {
   const params = {
     method,
     uri: `${this.endpoint}/${path}${this.getContentType('path')}`,
     json: true,
     resolveWithFullResponse: true
   }
   if (method !== 'GET') {
     params.body = body
     params.headers = await this.getHeader()
   }
   return params
 }
 /**
  * call request lib to call the api
  * @param {object} requestParam request param
  */
 sendRequest(requestParam) {
   return request(requestParam)
 }
 /**
  * Call api to send a request
  * @param {string} method HTTP method
  * @param {string} path request path
  * @param {object} [node={}] Added Drupal content
  * @return {Promise<{}>}
  */
 async request(method, path, node = {}) {
   const requestParam = await this.getRequestParam(method, path, node)
   return this.sendRequest(requestParam)
 }
}

const client = new RESTClient(endpoint, format)
client.configureBasicAuth(username, password)
client.request('POST', 'entity/node', newNode)
 .then(result => {
   console.log(result.statusCode)
   console.log(result.body)
 })
 .catch(result => {
   console.log(result.statusCode)
   console.log(result.body)
   console.log(result.message)
   console.log(result.error)
 })

DrupalのAPIを呼び出す際のポイント

POSTリクエストの際は以下のHeaderを送信する必要があります。

'Content-Type': ‘’,
'Authorization': ‘’
'X-CSRF-Token': ‘’

CSRF-Tokenの値は、`https://YOUR_DRUPAL.COM/rest/session/token`に対してGETリクエストを行うことで取得できます。

先ほどのファイルを`node index.js`などで実行すると、”Hello REST API”というタイトルのコンテンツ(type: article)が作成されます。失敗した場合は、ユーザー情報やAPIの権限設定に問題がある可能性がありますので、エラーメッセージをご確認ください。

HTMLコンテンツを投稿する

これまでの手順を応用して、body: { value: “hoge”}のように記述することで本文も追加することができます。ですがこのやり方の場合、HTMLタグを利用することができません。

Hello HTML tags

HTMLタグを含めてPOSTしたい場合は以下のような書き方をする必要があります。

const newNode = {
...
  body: [{
    format: 'basic_html',
    value: `This is example post.

これでHTMLタグを含めたコンテンツを作成することができるようになりました。

Hello HTML tags3

参考:https://drupal.stackexchange.com/questions/234553/8-3-how-to-create-a-node-via-rest-with-html

 
フッターの採用情報
 
Yoshikazu Aoyamaの写真

この記事を書いた人: Yoshikazu Aoyama

昔は回線交換やL2/L3のプロトコルスタックの開発をしてました。その後、組み込みLinuxやJava/Ruby on RailsなどのWebシステム開発などを経て現職。
インフラからDrupalのモジュール開発、Drupal以外の開発までなんでもやります。
普段は札幌で猫と一緒にリモートワークしています。 好きなモジュールは Restful Web Services と Rules

関連コンテンツ