Heroku ConnectでSalesforceデータを簡単に読み書きする

Heroku Connectを使ってSalesforeデータの読み書きの実装。Heroku ConnectのSalesforce Herokuの双方向の同期設定方法とNode.jsによる読み書きの簡易なサンプルです。
2021.01.18

前回では、Heroku Connectを使ってSalesforceのデータを簡単に読み出してみました。さて、Heroku ConnectはSalesforceデータの読み出しだけでなく書き込みもできますので、今回はHeroku Connectを使ってSalesforeデータの読み書きを実装してみたいと思います。

Salesforce、Heroku Connectの設定変更

前回の設定ではSalesforce => Herokuの単方向の同期設定になっているので、Salesforce <=> Herokuの双方向の同期設定に変更します。

Salesforce側の設定

Heroku Connectからの書き込みにも対応するためには、Salesforceの同期対象オブジェクト(この記事では取引先責任者(Contact))にユニークかつ外部IDに指定された項目が一つ必要です

ここでは、取引先責任者にExternalIdという項目を追加しました。

取引先責任者に設定した外部ID項目

Heroku Connect側の設定

Heroku ConnectのMappingsページにアクセスします。

Heroku ConnectのMappings画面

All Mappingsの中からContactを選択します。

Heroku ConnectのMappings画面でContactを選択した状態

Editボタンが表示されるのでクリックします。

Heroku ConnectのContactの同期設定画面

ここで、Database => Salesforceの「Write database updates to Salesforce using [--none--] as the unique identifier」にチェックを入れ、[--none--]のセレクトボックスにて、[ExternalId__c]を指定します。

また、Salesforce => Databaseの「Accelerate Polling」にもチェックを入れておきます。この設定によってSalesforce => Heroku Connectへのデータの同期が10分間隔のポーリングのタイミングをまたず、ほぼリアルタイムで反映されるようになります。

Heroku Connectを使った取引先責任者情報の書き込み

POSTパラメータを処理するために、Node.jsのbody-parserモジュールをインストールします。

$ npm install body-parser

./index.jsをテキストエディタで開き、次の差分を適用します。

$ git diff index.js
diff --git a/index.js b/index.js
index be57411..9a188bd 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,9 @@
-const express = require('express')
+const express = require('express');
+let bodyParser = require('body-parser');
+let app = express();
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(bodyParser.json());
+
 const path = require('path')
 const PORT = process.env.PORT || 5000

@@ -8,7 +13,7 @@ const pool = new Pool({
   ssl: { rejectUnauthorized: false }
 });

-express()
+app
   .use(express.static(path.join(__dirname, 'public')))
   .set('views', path.join(__dirname, 'views'))
   .set('view engine', 'ejs')
@@ -25,4 +30,23 @@ express()
         res.send("Error " + err);
       }
     })
+  .get('/db/new', (req, res) => res.render('pages/db/new'))
+  .post('/db/new/exec', async (req, res) => {
+      res.setHeader('Content-Type', 'text/plain');
+      try {
+        console.log(req.body);
+        const client = await pool.connect();
+        const query = {
+          text: 'INSERT INTO salesforce.contact (LastName, FirstName, Email) VALUES ($1, $2, $3)',
+          values: [req.body.LastName, req.body.FirstName, req.body.Email],
+        };
+        await client.query(query, () => {
+          client.release();
+          res.redirect(req.baseUrl + '/db');
+        });
+      } catch (err) {
+        console.error(err);
+        res.send("Error " + err);
+      }
+    })
   .listen(PORT, () => console.log(`Listening on ${ PORT }`))

また、次のテンプレートを./views/pages/db/new.ejsとして保存します。

<!DOCTYPE html>
<html>
<head>
  <% include ../../partials/header.ejs %>
</head>

<body>

<% include ../../partials/nav.ejs %>

<div class="container">
<h2>New Record</h2>

<form action="/db/new/exec" method="post">
  <input type="text" placeholder="LastName" name="LastName">
  <input type="text" placeholder="FirstName" name="FirstName">
  <input type="text" placeholder="Email" name="Email">
  <button type="submit">Submit</button>
</form>

</div>

</body>
</html>

行おうとしていることはシンプルで、https://<your-app-name>.herokuapp.com/db/new/へのアクセスで新規取引先責任者作成画面を開き、フォームの入力項目としてLastName(姓)、FirstName(名)、Email(メール)を受け付け、action先として/db/new/execを指定しています。

フォームを実行すると./index.js/db/new/execに処理が移り、INSERT INTO salesforce.contact (LastName, FirstName, Email) VALUES ($1, $2, $3)でフォームから受け取ったLastName、FirstName、Emailをsalesforce.contactにINSERTすることでSalesforceへの書き込みを行っています。

書き込みを行った後はres.redirect(req.baseUrl + '/db');https://<your-app-name>.herokuapp.com/db/(一覧画面)にリダイレクトしています。

なお、一覧画面のテンプレート./views/pages/db.ejsに次の差分を反映して、姓名、メールの表示、そして新規作成画面へのリンクを追加しています。

diff --git a/views/pages/db.ejs b/views/pages/db.ejs
index 27a5f49..4efcaec 100644
--- a/views/pages/db.ejs
+++ b/views/pages/db.ejs
@@ -9,11 +9,14 @@
 <% include ../partials/nav.ejs %>

 <div class="container">
+<h2>Manupilation</h2>
+<a href="/db/new">Create New</a>
+
 <h2>Database Results</h2>

 <ul>
     <% results.forEach(function(r) { %>
-        <li><%= r.id %> - <%= r.name %></li>
+      <li><%= r.id %> - <%= r.lastname %> <%= r.firstname %> ( <%= r.email %> )</li>
     <% }); %>
 </ul>

編集が終わったらコミットしてプッシュします。

$ git add -A .
$ git commit -m "add create function" -v
$ git push heroku

https://<your-app-name>.herokuapp.com/db/にブラウザでアクセスして、接続したSalesforce組織の取引先責任者情報が一覧表示されることを確認します。

Heroku Connectによる取引先責任者の一覧画面

Create Newのテキストリンクをクリックして、取引先責任者の作成画面が開くことを確認します。

Heroku Connectによる取引先責任者の新規作成画面

LastName、FistName、Emailに値を指定してSubmitボタンをクリックします。 画面が一覧画面に戻り、末尾に新しい取引先責任者が追加されていることを確認します。

末尾に新しい取引先責任者が追加された一覧画面

Salesforceにアクセスして、取引先責任者に指定したLastName、FirstName、Emailを持つ新規レコードが作成されていることを確認します。

Salesforceに新規追加された取引先責任者

Salesforce上からこのレコードのEmailを更新してみます。

Salesforce上でEmailを更新した取引先責任者

https://<your-app-name>.herokuapp.com/db/に再アクセスして、追加した取引先責任者のEmailが更新されていることを確認します。

末尾の取引先責任者のEmailが更新された一覧画面

取引先責任者の姓、名、メールが同期対象になっているので、Salesforce上での姓、名の更新も直ちにHeroku側に反映されます。さらに、Salesforce上での取引先責任者の追加、削除も同期されますので試してみてください。

まとめ

Heroku ConnectによってHerokuとSalesforce組織を接続し、Salesforceの取引先責任者(Contact)の情報を読み書きする方法についてみてみました。簡単にSaleforceの情報をPaaSであるHerokuで読み書きして、アプリケーションで利用できることがおわかりいただけたかと思います。

Heroku <=> Salesforce間がほぼリアルタイムで同期されますので、外部システムとSalesforceを連携させる際にHeroku Connectは有力な選択肢の一つになると思います。