JSforce AJAX Proxy を Next.js の API として実装してみた

2023.01.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

JavaScriptを利用してSalesforceからデータを取得する際に、著名なライブラリとして「JSforce」があります。

こちらのライブラリは、Node.jsにも対応しているのでNext.jsでも利用することができます。

Next.jsで利用する場合には、Salesforceの外部ウェブアプリケーションとなるため、接続をおこなうためには外部ドメインからのアクセスについて対応する必要があります。

方法としては2つあり、1つは 「Salesforce上でCORS(Cross-Origin Resource Sharing)設定を追加して、特定ドメインからのアクセスを許可する」 という方法、もう1つは 「CORSに対応したプロキシサーバーを経由させる」 という方法です。

今回は「CORSに対応したプロキシサーバーを経由させる」方法を取りたいのですが、JSforceはドキュメントの以下に記載されている通りプロキシサーバーに対応しています。

こちらに記載されているとおり、プロキシサーバーとしては「JSforce AJAX Proxy」が以下に用意されており、Node.jsのアプリや、expressのMiddlewareとして利用することができます。

一方で、Next.jsで利用する場合にはAPI利用の仕組みとしてAPI Routesが存在するので、プロキシサーバーをAPI RoutesのAPIとして実装したくなります。

今回は、この jsforce-ajax-proxy を Next.js の API として実装してみたので、対応方法について記載したいと思います。

前提

大前提として、JSforceでのSalesforceへの接続にはOAuth認証を利用しています。

Salesforce上では「接続アプリケーション」を作成し、「コールバック URL」を設定する必要がありますが、「コールバック URL」には「https」のみ指定が可能なため、開発時のNext.jsアプリはHTTPSアクセスが可能なようにしています。

開発時のNext.jsアプリのHTTPS化については、以下もご参照ください。

Proxy APIの実装

いきなりですが、以下が実装内容となります。「JSforce AJAX Proxy」の実装を元にNext.jsのAPIとして実装しています。

pages/api/proxy/salesforce.ts

import { NextApiRequest, NextApiResponse } from 'next'

/**
 * Allowed request headers
 */
const ALLOWED_HEADERS = [
  'Authorization',
  'Content-Type',
  'Salesforceproxy-Endpoint',
  'X-Authorization',
  'X-SFDC-Session',
  'SOAPAction',
  'Sforce-Auto-Assign',
  'Sforce-Call-Options',
  'Sforce-Query-Options',
  'x-sfdc-packageversion-clientPackage',
  'If-Modified-Since',
  'X-User-Agent',
]

/**
 * Endpoint URL validation
 */
// eslint-disable-next-line no-useless-escape
const SF_ENDPOINT_REGEXP = /^https:\/\/[a-zA-Z0-9\.\-]+\.(force|salesforce|cloudforce|database)\.com\//

/**
 * Salesforce OAuth Token Request Parameter Type
 */
type RequestBodyType = {
  grant_type: string
  code: string
  client_id: string
  redirect_uri: string
  client_secret: string
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PATCH,PUT,DELETE')
  res.setHeader('Access-Control-Allow-Headers', ALLOWED_HEADERS.join(','))
  res.setHeader('Access-Control-Expose-Headers', 'SForce-Limit-Info')
  if (req.method === 'OPTIONS') {
    res.end()
    return
  }

  const sfEndpoint = req.headers['salesforceproxy-endpoint'] as string
  if (!SF_ENDPOINT_REGEXP.test(sfEndpoint)) {
    res.status(400)
    res.send(
      'Proxying endpoint is not allowed. `salesforceproxy-endpoint` header must be a valid Salesforce domain: ' +
        sfEndpoint
    )
    return
  }

  const headers = {}
  ALLOWED_HEADERS.forEach(function (header) {
    header = header.toLowerCase()
    const value = req.headers[header]
    if (value) {
      const name = header === 'x-authorization' ? 'authorization' : header
      headers[name] = req.headers[header]
    }
  })

  const url = sfEndpoint || 'https://login.salesforce.com//services/oauth2/token'
  const requestBodyParams: RequestBodyType = req.body
  const params = {
    method: req.method,
    headers: headers,
    body: req.method === 'POST' ? JSON.stringify(requestBodyParams) : null,
  }

  const fetchRes = await fetch(url, params)
  const resJson = await fetchRes.json()
  res.status(fetchRes.status).json(resJson)
}

なお、ここではAPIパスを/api/proxy/salesforceとしていますがパスは任意です。

fetchを利用したリクエスト周りは改良の余地がありそうですが、一旦はこの実装で最低限の利用はできるかと思います。

Proxy APIを経由してSalesforceへアクセスする

あとはこのAPIを経由してSalesforceへアクセスするだけです。

ここでは詳細は解説しませんが、最初にOAuth認証をした後に、以下のようにコネクションを張るだけとなります。

import jsforce from 'jsforce'

const conn = new jsforce.Connection({
  oauth2 : {
    clientId : '<your Salesforce OAuth2 client ID is here>',
    clientSecret : '<your Salesforce OAuth2 client secret is here>',
    redirectUri : '<your Salesforce OAuth2 redirect URI is here>'
  },
  proxyUrl: '/api/proxy/salesforce'
  instanceUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>',
  accessToken : '<your Salesforce OAuth2 access token is here>',
  refreshToken : '<your Salesforce OAuth2 refresh token is here>'
});

コネクション作成時には、先程作成したAPIパスをproxyUrlとして指定してあげることで、作成したプロキシ経由でSalesforceにアクセスすることが出来るようなります。

まとめ

以上、JSforce AJAX Proxy を Next.js の API として実装してみました。

Next.jsでJSforceを利用した情報はなかなか見当たらなかったので、試行錯誤を繰り返していますが今後も何か関連情報をアウトプットしたいと思います。

どなたかのお役に立てば幸いです。それでは!