Salesforceで取引が成立しても、サイトを本番公開するには別途手動での作業が必要です。開発者MyKinstaで対象のクライアントサイトを特定し、バックアップを作成したうえで、ステージング環境を本番環境に反映しなければなりません。

Kinsta APIを利用すれば、これら2つのワークフローを連携させ、案件の承認をきっかけに公開プロセスを自動的に開始できます。担当者が各手順を実行する代わりに、オポチュニティが指定したステージに到達した時点でプロセスが自動的に実行されます。

Salesforceの商談(Opportunity)が更新されると、MyKinstaで一連のタスクが自動的に実行されるため、公開後の手動作業は不要になります。一方、クライアントはチーム間の引き継ぎを待つことなく、承認から本番公開までをよりスピーディーに進めることができます。

この手順に必要なもの

これからご紹介する手順では、ステージング環境と本番環境の両方を備えたWordPressサイトを含むMyKinstaアカウントFlow Builderを利用できるSalesforceアカウント、そしてミドルウェアを実行するためにローカル環境へインストールしたNode.jsが必要になります。

Kinsta APIを利用するには、MyKinstaにログイン後、「企業の設定」>「APIキー」 に移動し、「APIキーを作成」 をクリックしてAPIキーを生成してください。

MyKinstaのAPIキー画面
MyKinstaのAPIキー画面

キーに名前を付け、有効期限を設定し、「生成」をクリックします。なお、キーは一度しか表示されないため、モーダルを閉じる前に必ずコピーしてください。プロジェクトルートにある.envファイルに「企業の設定」>「請求先情報」にある「企業ID」と一緒に保存しておきます。

KINSTA_API_KEY=your_api_key_here
KINSTA_COMPANY_ID=your_company_id_here

また、各クライアントプロジェクトのKinstaサイトIDを保存するため、Salesforceの商談(Opportunity)オブジェクトにカスタムテキストフィールドを作成する必要があります。「Setup(設定)」>「Object Manager(オブジェクトマネージャー)」に移動し、「Opportunity(商談)」>「Fields and Relationships(項目とリレーション)」を開きます。

Salesforceの設定画面内にある「Fields & Relationships(項目とリレーション)」ページ
Salesforceの設定画面内にある「Fields & Relationships(項目とリレーション)」ページ

ここで「Field Label(フィールドラベル)」を入力すると、「Field Name(フィールド名)」が自動生成されます。このフィールド名は後で使用するため、控えておいてください。「Length(長さ)」は「255」に設定し、変更を保存します。

サイトIDは、サイト作成時にKinstaによって割り当てられるUUIDです。サイトを開いた際にMyKinstaのURLから確認できるほか、APIキーを指定して GET /sitesエンドポイントを呼び出すことで取得できます。

https://my.kinsta.com/sites/details/hyut4927-d324-4044-b794-67ap0rbf20bj/…

ワークフロー全体をトリガーするには、各商談のカスタムフィールドでサイトIDを使用します。

Kinsta APIを活用してSalesforceからWordPress運用を自動化する方法

Salesforce側では、レコードトリガーフローを使用して商談のステージを監視し、ステージが特定の状態に遷移した際にHTTPコールアウトを実行します。

Node.jsミドルウェアはサイトIDを受け取り、Kinsta APIを呼び出してステージング環境のバックアップを作成します。その後、バックアップ処理の完了を待ってから、ステージング環境を本番環境へ反映します。このワークフローの大部分はSalesforce上で構築されるため、事前に必要な権限とアクセス設定を確認しておきましょう。

1. 指定クレデンシャル(Named Credentials)の設定

Salesforceには、APIキーなどの認証情報を安全に管理する仕組みが用意されています。これには、実際のシークレットを保存する「外部クレデンシャル(External Credentials)」と、接続先のエンドポイントURLを定義する「名前付きクレデンシャル(Named Credentials)」が含まれます。

Salesforceのホーム画面から、「Setup」を開きます。設定(Setup)を開きます。

ツールバーに表示される「Setup」アイコン(歯車の形)
ツールバーに表示される「Setup」アイコン(歯車の形)

ここで「Named Credentials」を検索し、「External Credentials」タブを開いて「New(新規)」をクリックします。名前とラベルを入力し、認証プロトコルを「Custom(カスタム)」に設定してください。これにより、Salesforceの管理対象OAuthフローを使用する代わりに、Bearerトークンヘッダーを手動で定義できるようになります。

保存したら、「Principals」セクションまでスクロールし、「New」をクリックします。KinstaKeyなどの名前を入力し、値としてKinsta APIキーを設定してください。

名前、ラベル、認証プロトコルのフィールド
名前、ラベル、認証プロトコルのフィールド

次に、「Custom Header(カスタムヘッダー)」を追加します。ヘッダー名には Authorization を指定し、値には先ほど作成したPrincipalを参照する設定を行います。これにより、外部APIへのすべてのコールアウトで、Kinsta APIキーがBearerトークンとして自動的に送信されるようになります。

各種フィールドと認証オプションが表示された「New Named Credential」画面
各種フィールドと認証オプションが表示された「New Named Credential」画面

External Credentialを保存したら、「Named Credentials」タブを開いて「New」をクリックします。ミドルウェアのエンドポイントURLを入力し、必要な項目を設定したうえで、「Authentication」セクションから先ほど作成した「External Credential」を選択してください。

ユーザー権限の設定

External CredentialのPrincipalを利用するには、対応するPermission Set(権限セット)も設定する必要があります。これにより、ユーザーにKinsta APIを呼び出すための認証情報へのアクセス権が付与されます。設定するには、「Setup」>「Permission Sets」に移動し、「New」をクリックします。

Permission Setの名前を入力して保存したら、再度そのPermission Setを開き、「External Credential Principal Access(外部クレデンシャルのPrincipalアクセス権)」画面を編集します。ここで、External CredentialのPrincipalを有効化リストへ移動してください。

無効なリストと有効なリストが表示された「External Credential Principal Access」画面
無効なリストと有効なリストが表示された「External Credential Principal Access」画面

最後に変更を保存し、「Permission Set」画面に戻ります。画面上部のツールバーにある「Manage Assignments(割り当ての管理)」をクリックしてください。

Salesforceツールバーの「Manage Assignments」リンク
Salesforceツールバーの「Manage Assignments」リンク

この画面で「Add Assignment(割り当ての管理)」をクリックし、ユーザープロファイルに割り当てることで、Kinsta APIへのアクセスを有効化します。

2. 商談オブジェクトにレコードトリガーフローを作成

次に、Salesforce App Launcher(アプリケーションランチャー)を開き、「Flows(フロー)」を検索します。「New」をクリックし、「Record-Triggered Flow(レコードトリガーフロー)」を選択してください。

自動化を構築するための各種オプションとともに表示される「Record-Triggered Flow」
自動化を構築するための各種オプションとともに表示される「Record-Triggered Flow」

「Flow Builder(フロービルダー)」が開いたら、以下のように設定します。

  • オブジェクトに「Opportunity」を選択する
  • レコードが更新されたときにトリガーされるよう設定する
  • 「Condition Requirements(条件要件)」で「All Conditions Are Met (AND)(すべての条件を満たす)」を選択する
  • 表示された条件フィールドで、以下を設定する
    • Field(項目):Stage
    • Operator(演算子):Equals
    • Value(値):Closed Won
  • 「When to Run the Flow for Updated Records(更新されたレコードに対してフローを実行するタイミング)」で、「Only when a record is updated to meet the condition requirements(レコードが条件を満たすように更新された場合のみ) 」を選択する

この設定により、フローは条件を満たしたタイミングでのみ実行されます。レコードの更新をトリガーとして使用することで、デプロイ処理が複数回実行されるのを防ぐことができます。この設定を行わない場合、ステージ変更後にレコードが保存されるたびにフローが実行されてしまいます。

「Record-Triggered Flow」の設定が完了した「Flow Builder」画面
「Record-Triggered Flow」の設定が完了した「Flow Builder」画面

最後に、「Optimize the Flow For(フローの最適化対象)」で 「Actions and Related Records(アクションおよび関連レコード)」 を選択します。続いて、「Add Asynchronous Path(非同期パスを追加)」を有効にしてください。これにより、HTTPコールアウトを実行できるようになり、新たに2つのパスが表示されます。

3. 非同期パスを設定し、HTTPコールアウトアクションを追加

Salesforceでは、トリガートランザクションの実行中にHTTPコールアウトを行うことはできません。そのため、コールアウトは必ず「Run Asynchronously(非同期で実行)」パスに配置する必要があります。このパスに配置されたアクションは、トリガーとなったトランザクションのコミット後に実行されます。

「Run Immediately」と「Run Asynchronously」の2つのパスが表示された「Flow Builder」画面
「Run Immediately」と「Run Asynchronously」の2つのパスが表示された「Flow Builder」画面

Run Asynchronously(非同期で実行)」パス上に「Action(アクション)」要素を追加し、右側パネルの下部にある「Create HTTP Callout(HTTPコールアウトを作成)」を選択します。

パス上の「Action」要素に対する操作を表示した「Search Actions」パネル
パス上の「Action」要素に対する操作を表示した「Search Actions」パネル

コールアウトの設定では、名前を入力し、URLにミドルウェアのエンドポイントを指定します。パスには /go-liveを使用してください。ミドルウェアをまだデプロイしていない場合は、一時的なURLを設定しておくこともできます。ローカル開発環境では、ngrokを使用してローカルポートを公開URLとして公開できます。また、この画面で「Named Credential」も選択してください。

Next」をクリックしたら、HTTPメソッドに POSTを選択し、コールアウトのラベルを設定します。続いて、リクエストとレスポンスのサンプルJSONを入力する必要があります。まずはリクエスト用として、以下のJSONを使用します。

{
  "site_id":"fbab4927-e354-4044-b226-29ac0fbd20ca"
}

次の画面で「Connect with Sample Response(サンプルレスポンスで接続)」を選択すると、「Connect(接続)」ボタンを使用してここまでの接続設定をテストできます。ただし、ミドルウェアを実装するまでは502エラーが表示されます。ここでは「Use Example Response(サンプルレスポンスを使用)」をクリックし、以下の内容を入力してください。

{
  "message":"Received"
}

接続をさらにテストしたい場合は、後でこの画面に戻って接続を確認できます。

4. Flow Builderでリクエストボディを設定

「Set Request Body」ドロップダウンメニューを表示した「Flow Builder」画面
「Set Request Body」ドロップダウンメニューを表示した「Flow Builder」画面

ここで名前(例:requestBody)を入力して保存し、そのリソースをリクエストボディの「Value」として選択します。次に、「Flow Builder」で「Assignment(割り当て)」要素を追加し、ラベルと名前を設定してください。その後、「Set Variable Values(変数値を設定)」のドロップダウンメニューで以下を設定します。

  • Variable(変数):site_id
  • Operator(演算子):Equals
  • Value(値):「Triggering Opportunity(トリガーされた商談)」サブメニューをスクロールし、「Kinsta Site ID」を選択

これでSalesforce側の設定は完了です。次はNode.jsアプリケーションの構築に進みます。

5. Node.jsミドルウェアを構築

フローの設定が完了したら、次はミドルウェアを構築します。Kinsta APIへの呼び出しは、このミドルウェア内で実行されます。まずは新しいNode.jsプロジェクトを作成し、必要な依存関係をインストールします。

npm init -y
npm install express dotenv

Express.jsは、ルーティングやリクエストの解析を処理します。dotenvは.envファイルを読み込み、APIキーをソースコードに直接記述することなく、実行時に利用できるようにします。次に、プロジェクトのルートディレクトリにapp.jsを作成してください。

// app.js
const express = require('express');
require('dotenv').config();
const app = express();
app.use(express.json());


const KINSTA_API_URL = 'https://api.kinsta.com/v2';


const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${process.env.KINSTA_API_KEY}`
};

app.post('/go-live', async (req, res) => {
  const { site_id } = req.body;
  if (!site_id) {
    return res.status(400).json({ message: 'site_id is required' });
  }
  // Kinsta API calls added in the steps below
  res.status(200).json({ message: 'Received' });
});

app.listen(3000, () => console.log('Middleware running on port 3000'));

headers定数は、アプリケーション内で実行されるすべてのKinsta APIリクエストに対して、Bearerトークン認証を設定するために使用します。なお、GET /sitesなどのエンドポイントで必要となる企業IDは、Authorizationヘッダーではなくクエリパラメータとして渡します。また、冒頭のrequire('dotenv').config()は、他の処理が実行される前に.envファイルからAPIキーを読み込むためのものです。

バックアップを作成する前に、ミドルウェアはステージング環境と本番環境の両方の環境IDを取得する必要があります。headers定数の下に、getEnvironments関数を追加してください。

const getEnvironments = async (siteId) => {
  const resp = await fetch(
    `${KINSTA_API_URL}/sites/${siteId}/environments`,
    { method: 'GET', headers }
  );

  const data = await resp.json();
  return data.site.environments;
};

この関数はGET /sites/{siteId}/environmentsを呼び出し、環境情報を含む完全なenvironments配列を返します。

6. ステージング環境の手動バックアップを作成

環境を本番環境にプッシュすると、本番サイトが上書きされます。事前にバックアップを作成しておくことで、ステージングでのテストでは検出できなかった競合がプッシュ時に発生した場合でも、復元ポイントを確保できます。

ここでは、getEnvironmentsの下にcreateBackup関数を追加します。

const createBackup = async (envId) => {
  const resp = await fetch(
    `${KINSTA_API_URL}/sites/environments/${envId}/manual-backups`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({ tag: 'pre-launch-backup' })
    }
  );

  const data = await resp.json();
  return data;
};

Kinstaではバックアップが非同期で処理されるため、完了済みの結果ではなく、operation_idを含む202 Acceptedレスポンスが返されます。

{
  "operation_id": "backups:add-manual-54fb80af-576c-4fdc-ba4f-b596c83f15a1",
  "message": "Adding a manual backup to environment in progress",
  "status": 202
}

プッシュ処理を実行する前にバックアップの完了を待機するため、createBackupの下にpollOperation関数を追加します。

const pollOperation = async (operationId, intervalMs = 5000, maxAttempts = 12) => {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await new Promise(resolve => setTimeout(resolve, intervalMs));
    const resp = await fetch(
      `${KINSTA_API_URL}/operations/${operationId}`,
      { method: 'GET', headers }
    );
    const data = await resp.json();
    if (data.status === 200) return data;
    if (data.status >= 400) throw new Error(`Operation failed: ${data.message}`);
  }
  throw new Error('Operation timed out');
};

このループは5秒ごとに処理状況を確認し、最大1分間待機します。Operationsエンドポイントから200ステータスが返された時点でバックアップは完了しており、プッシュ処理を続行できます。

7. ステージングを本番環境にプッシュし、完了を監視

バックアップの完了を確認したら、pollOperationの下にpushToProduction関数を追加します。

const pushToProduction = async (siteId, stagingEnvId, liveEnvId) => {
  const resp = await fetch(
    `${KINSTA_API_URL}/sites/${siteId}/environments`,
    {
      method: 'PUT',
      headers,
      body: JSON.stringify({
        source_env_id: stagingEnvId,
        target_env_id: liveEnvId,
        push_db: true,
        push_files: true,
        run_search_and_replace: true
      })
    }
  );
  const data = await resp.json();
  return data;
};

source_env_idtarget_env_idは、それぞれプッシュ元とプッシュ先の環境を指定するためのパラメータです。run_search_and_replaceフラグは、プッシュ後にデータベース内のハードコードされたドメイン参照を更新します。このフラグを有効にしない場合、ステージング環境のドメイン参照がデータベース内に残り、本番環境へのプッシュ完了後もそのまま使用されることになります。

このプッシュ処理も、operation_idを含む202 Acceptedレスポンスを返します。このoperation_idpollOperationに渡すことで、処理の完了を確認できます。

最後に、ルートハンドラーを更新し、すべての関数を順番に実行するようにします。

app.post('/go-live', async (req, res) => {
  const { site_id } = req.body;
  if (!site_id) {
    return res.status(400).json({ message: 'site_id is required' });
  }
  try {
    const environments = await getEnvironments(site_id);
    const stagingEnv = environments.find(env => env.name === 'staging');
    const liveEnv = environments.find(env => env.name === 'live');
    const backup = await createBackup(stagingEnv.id);
    await pollOperation(backup.operation_id);
    const push = await pushToProduction(site_id, stagingEnv.id, liveEnv.id);
    await pollOperation(push.operation_id);
    console.log(`Go-live complete for site ${site_id}`);
    res.status(200).json({ message: 'Go-live complete' });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: 'Go-live failed', error: err.message });
  }
});

変更を保存したら、必要に応じて「Named Credential」を実際のミドルウェアURLに更新し、フローを有効化します。次に、node app.jsを実行し、Salesforceで「Opportunity」を対象のステージに移動します。

本番環境へのプッシュを実行中のMyKinstaの様子
本番環境へのプッシュを実行中のMyKinstaの様子

MyKinstaにログインしなくても、サイトを本番公開できるようになります。また、Salesforceの「Headless 360」を利用すれば、このワークフローの大部分をGUIではなく、CLIやMCP経由で実行することも可能です。

SalesforceとKinstaを活用して制作・開発ワークフローを自動化

Node.jsミドルウェアを介してKinsta APIとSalesforceを連携することで、自動化されたデプロイワークフローを構築できます。SalesforceでOpportunity(商談)のステージを変更すると、MyKinstaが自動的にバックアップを作成し、本番環境へのプッシュを実行して、その完了まで確認します。手動での操作は必要ありません。

ミドルウェアを本番環境にデプロイする際は、Sevallaを使ってみてください。利Node.jsサービス向けに設計されたプラットフォームで、プロジェクトをGitプロバイダにプッシュし、リポジトリを接続して環境変数を設定するだけで利用可能です。その後、SalesforceのHTTPコールアウトURLを本番環境のミドルウェアURLに更新してください。

複数のクライアントサイトを管理しながら自動化を進める制作会社や開発会社向けに、Kinstaでは、エージェンシーパートナープログラムを提供しています。専任サポートやパートナー向け特典を活用することで、このような自動化ワークフローを大規模に運用しやすくなります。

Joel Olawanle Kinsta

Kinstaでテクニカルエディターとして働くフロントエンド開発者。オープンソースをこよなく愛する講師でもあり、JavaScriptとそのフレームワークを中心に200件以上の技術記事を執筆している。