API

外部システムからApexクラスをREST APIで呼ぶ方法

Apexクラスに@RestResourceを付けて公開し、外部クライアントアプリの認証を通してREST APIとして呼び出す手順を説明します。

2023.02.10

なぜApexを自前のREST APIにするのか

標準のREST APIは取引先や商談など、標準・カスタムオブジェクトをそのまま公開します。複数のオブジェクトをまとめて返す、独自の計算をはさむ、といった処理は標準APIだけでは足りません。そういうときは、Apexクラス自体をREST APIとして公開します。

Apexクラスを公開する書き方

クラスに @RestResource を付け、global 修飾子で宣言します。urlMapping はスラッシュで始まる相対パスで、末尾に /* を置くとワイルドカードとして後続のパスを受け取れます。

@RestResource(urlMapping='/Account/*')
global with sharing class AccountRestResource {

    @HttpGet
    global static Account doGet() {
        RestRequest req = RestContext.request;
        String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/') + 1);
        return [SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId];
    }

    @HttpPost
    global static String doPost(String name, String phone, String website) {
        Account account = new Account(Name = name, Phone = phone, Website = website);
        insert account;
        return account.Id;
    }

    @HttpDelete
    global static void doDelete() {
        RestRequest req = RestContext.request;
        String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/') + 1);
        delete new Account(Id = accountId);
    }
}

取引先を1件返す・作成する・削除するREST APIです。with sharingを付けているので、共有ルールが適用されます。

@HttpPost のメソッドは、引数名がそのままリクエストボディのJSONキーと対応づき、Salesforce側が自動で割り当てます。@HttpGet と @HttpDelete のメソッドは引数を持てません。IDはURLから自分で取り出します。

1つのクラスに、同じHTTPメソッドのアノテーションを付けたメソッドを2つ以上置けません。GETを2通り用意したいときは、urlMappingを分けて別クラスにするか、パスの続きをメソッド内で判定します。

公開されるエンドポイント

Apex RESTのエンドポイントは、標準REST APIと違ってバージョン番号を含みません。urlMapping をそのまま続けた形になります。

https://xxxx.my.salesforce.com/services/apexrest/Account/{取引先ID}

Apex RESTのエンドポイントの形です。/services/data/のようなバージョン番号は付きません。

認証の組み方

プロファイルのシステム管理者権限一覧。APIの有効化とApex RESTサービスにチェックが入っている
呼び出す側のユーザに要る権限です。赤枠の「APIの有効化」と、その上の「Apex REST サービス」を見てください。

呼び出す側は、標準REST APIと同じ入口を使います。外部クライアントアプリでOAuth設定を有効化し、クライアントクレデンシャルフローかJWTベアラーフローでアクセストークンを取得します。取得したトークンを Authorization: Bearer に付けてApex RESTのURLを叩けば、通常のREST APIと同じように呼び出せます。ユーザ名パスワードフローは既定でブロックされているため使いません。

トークン取得の応答JSON。access_tokenやinstance_urlの値は黒く塗られている
応答で返る access_token を、以降のリクエストのAuthorizationヘッダーに付けます。

動作確認の手順

  1. 取引先を1件作る

    取得・削除を試す対象として、取引先を1件作っておきます。

  2. POSTで作成を試す

    アクセストークンを付けて、名前・電話番号・URLを送り、返ってきたIDを控えます。

  3. GETで取得を試す

    控えたIDをURLの末尾に付けてGETし、登録した内容がそのまま返ることを確かめます。

取引先「REST API 接続テスト用」の詳細画面。取引先の詳細の欄が開いている
確認用に作った取引先です。この画面のURLに入っているIDを、あとでリクエストの末尾に付けます。
APIクライアントの認証タブ。Tokenの入力欄に伏せられた文字列が入っている
取得したアクセストークンをTokenの欄に入れます。ヘッダーでは Bearer を前に付けた形になります。
APIクライアントのGETリクエスト。/services/apexrest/Account/ の末尾に取引先IDが付いている
URLの形を見てください。/services/apexrest/ のあとがurlMappingで、その末尾に取引先IDを付けます。
APIクライアントの応答ステータス表示。200 OKと出ている
ステータスが200 OKなら成功です。401が返るときはトークン、403なら権限を疑ってください。
curl -X POST "https://xxxx.my.salesforce.com/services/apexrest/Account/" \
  -H "Authorization: Bearer xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"REST API接続テスト用","phone":"0312345678","website":"https://example.com"}'

取引先を作成してからIDで取得します。Authorizationヘッダーにアクセストークンを付けます。

ここで間違えやすい

間違い何が起きるか
クラスを global にし忘れる外部から見えず、REST APIとして呼べません
@HttpGet のメソッドに引数を書くコンパイルが通りません。引数はURLから読み取ります
同じHTTPメソッドのアノテーションを2つ付けるコンパイルが通りません。クラスまたはパスを分けます
権限のないユーザで呼び出すApexは既定でシステムモードで動くため、with sharingで効くのは共有ルールだけです。オブジェクト権限・項目レベルセキュリティも効かせたいときは、SOQLやDMLをユーザモード(WITH USER_MODEなど)で実行します

確認した環境

  • 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、RestResourceアノテーションとApex RESTメソッドの制約を確認しています

まとめ

  • Apexクラスに @RestResource を付け global で宣言すると、REST APIとして公開できます
  • @HttpGet・@HttpDelete は引数を持てません。IDはURLから取り出します
  • @HttpPost の引数名は、リクエストボディのJSONキーとそのまま対応します
  • エンドポイントは /services/apexrest/ から始まり、標準REST APIと違いバージョン番号を含みません
  • 認証は標準REST APIと同じ外部クライアントアプリの入口を使います。ユーザ名パスワードフローは使いません

参考:当時の画面

記事を最初に書いた当時の画面です。いまの手順と違うところは、各画像の説明に書いています。

設定の接続アプリケーション画面。新規ボタンが並んだ見出し行だけが表示されている
当時は設定の「接続アプリケーション」から新規で作っていました。
新規接続アプリケーションの確認画面。反映に最大10分かかるという赤い注意文が出ている
保存後の確認画面です。設定が効くまで最大10分かかる点は、いまの外部クライアントアプリでも同じです。
接続アプリケーションの新規作成フォーム。OAuth設定の有効化にチェックが入り、コールバックURLとOAuth範囲が入力されている
入力の要は「OAuth 設定の有効化」とコールバックURL、そして選択したOAuth範囲の3つです。
作成した接続アプリケーションの詳細画面。コンシューマ鍵とコンシューマの秘密は黒く塗られている
作成後の詳細です。ここで発行されるコンシューマ鍵と秘密を、トークン取得のときに使います。
APIクライアントのトークン取得リクエスト。grant_typeがpasswordになっている
grant_typeが password になっている点に注目してください。このフローはいまは使いません。
GETの応答JSON。attributesとId、Nameが返っている
取引先1件がそのままJSONで返ります。Nameの末尾に1234が付いているのは当時のコードがGETで更新もしていたためです。

Salesforceの導入・運用についてご相談ください

導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。