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を設定することをオススメします。
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タグを利用することができません。
HTMLタグを含めてPOSTしたい場合は以下のような書き方をする必要があります。
const newNode = {
...
body: [{
format: 'basic_html',
value: `This is example post.
これでHTMLタグを含めたコンテンツを作成することができるようになりました。
参考:https://drupal.stackexchange.com/questions/234553/8-3-how-to-create-a-node-via-rest-with-html
関連コンテンツ
- 新しい古典:Jamstack と MACH が従来の CMS の概念に向け進化する
- デジタル庁
- Headless CMS というトレンドに Drupal は適応している!?
- State of Drupalプレゼンテーション(2021年4月)
- アクイアのサービスの中核を担う Cloud Platform とは
- 第 16 回 Drupal をもっと知りたい方に向けた各種情報
- 第 14 回 Drupal のテーマシステムについて
- 第 3 回 Drupal の特徴
- Contenta CMSによるDecoupled Drupalサイトの構築
- CMSの第四の波 Distributed CMS (Drupal Developer Days Transylvania 2019)
Drupal 初心者講座バックナンバー
- Drupal 9/10 初心者講座
- 第 1 回 歴史に見る Drupal の DNA
- 第 2 回 Drupal はフレームワークか?CMS か?他の CMS との比較
- 第 3 回 Drupal の特徴
- 第 4 回 Drupal 9 / 10 のインストール (1)
- 第 5 回 Drupal 9 / 10 のインストール (2)
- 第 6 回 Drupal にコンテンツを投稿してみる
- 第 7 回 Drupal のボキャブラリとタクソノミーの使い方
- 第 8 回 コンテンツ管理における Drupal と他の CMS との比較
- 第 9 回 Drupal のブロックシステム
- 第 10 回 Drupal の標準クエリビルダー Views の使い方
- 第 11 回 Drupal と他の CMS のクエリビルダー機能を比較
- 第 12 回 Drupal の多言語機能と他の CMS やサービスとの比較
- 第 13 回 Drupal の権限設定と WordPress や Movable Type との比較
- 第 14 回 Drupal のテーマシステムについて
- 第 15 回 Drupal の拡張モジュールの選定と使い方
- 第 16 回 Drupal をもっと知りたい方に向けた各種情報