結論
カスタムエンドポイントは、rest_api_init アクションの中で register_rest_route() を呼んで作ります。必須の要素は次の4つです。
- 名前空間(
ks/v1のように、プラグインやテーマ固有の名前+バージョン)。 - ルート(
/items/(?P<id>d+)のように、正規表現でパラメータを定義)。 methodsとcallback。permission_callback(公開なら__return_true、書き込みなら権限チェック)。
登録後は /wp-json/ks/v1/items/12 のような URL で呼べます(パーマリンクが基本設定なら ?rest_route=/ks/v1/items/12)。
原因・仕組み
REST API のリクエストは、WP_REST_Server がルートを照合し、permission_callback、引数の検証・サニタイズ、callback の順に処理します。公式リファレンスでは、ルート登録は rest_api_init の時点か、それ以降に行うこと、名前空間の先頭と末尾にスラッシュを付けないこと、permission_callback は必須であること(5.5.0 以降)が示されています。コールバックの戻り値は、配列やオブジェクトでも rest_ensure_response() を通して WP_REST_Response として扱われ、WP_Error を返すとエラー応答になります。
手順(サンプルコード)
投稿IDから公開記事のタイトルを返す GET と、編集者以上が使える POST の例です。
add_action( 'rest_api_init', function () {
register_rest_route( 'ks/v1', '/items/(?P<id>d+)', array(
'methods' => WP_REST_Server::READABLE,
'callback' => function ( WP_REST_Request $request ) {
$post = get_post( (int) $request['id'] );
if ( ! $post || 'publish' !== $post->post_status ) {
return new WP_Error( 'ks_not_found', '記事が見つかりません', array( 'status' => 404 ) );
}
return rest_ensure_response( array( 'id' => $post->ID, 'title' => get_the_title( $post ) ) );
},
'permission_callback' => '__return_true', // 公開データなので全員に許可
'args' => array(
'id' => array( 'validate_callback' => function ( $v ) { return is_numeric( $v ) && (int) $v > 0; } ),
),
) );
register_rest_route( 'ks/v1', '/notes', array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => function ( WP_REST_Request $request ) {
return new WP_REST_Response( array( 'saved' => $request->get_param( 'text' ) ), 201 );
},
'permission_callback' => function () { return current_user_can( 'edit_posts' ); },
'args' => array(
'text' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
),
) );
} );
フロントエンドから呼ぶときは、ログイン中のユーザーとして書き込むなら X-WP-Nonce ヘッダーに wp_create_nonce( 'wp_rest' ) の値を付けます。
fetch( ksRest.root + 'ks/v1/notes', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': ksRest.nonce },
body: JSON.stringify( { text: 'メモ' } ),
} );
動作確認(検証環境と結果)
WordPress 7.1.2、PHP 8.2.12、MariaDB 10.4.32 で、rest_do_request() を使い、HTTP を介さずサーバー内でリクエストを再現しました(実行: php C:Tempwp-testrun.php rest-api-custom-endpoint-how-to.php)。
| リクエスト | ステータス | 応答 |
|---|---|---|
| GET /ks/v1/items/(公開記事のID) | 200 | {"id":6,"title":"テスト記事"} |
| GET /ks/v1/items/999999 | 404 | ks_not_found(自作の WP_Error) |
| GET /ks/v1/items/abc | 404 | rest_no_route(正規表現 d+ に一致せず) |
POST /ks/v1/notes(管理者、text は <b>hi</b> there) |
201 | {"saved":"hi there"}(タグが除去された) |
| POST /ks/v1/notes(text なし) | 400 | rest_missing_callback_param |
| POST /ks/v1/notes(未ログイン) | 401 | rest_forbidden |
rest_url( 'ks/v1/items/1' ) は、パーマリンク未設定の状態では http://localhost/index.php?rest_route=/ks/v1/items/1 を、パーマリンクを /%postname%/ にした状態(レビュー時の再実行)では http://localhost/wp-json/ks/v1/items/1 を返しました。URL は文字列で組み立てず、rest_url() で取得するのが確実です。実際のネットワーク越しの呼び出しと、Cookie+nonce 認証のブラウザ動作は、この環境では確認していません。
REST API カスタムエンドポイントの作り方の注意点
- 名前空間はプラグインやテーマ固有にし、
wp/v2など core の名前空間は使いません。 permission_callbackを省略すると、デバッグ通知が出ます(詳細はpermission_callback がない警告の対処)。- 認証が必要な書き込み系では、Cookie 認証なら nonce が必要です。アプリケーションパスワードを使う外部連携では、Basic 認証が使えます。
sanitize_callbackは値を整える処理で、安全性の最終保証にはなりません。出力時のエスケープも別に行います。- 大量データを返す場合は、ページネーション用の引数(
per_pageなど)を用意します。
REST API カスタムエンドポイントの作り方でよくあるミス
register_rest_route()をrest_api_initの外(functions.php の直下)で呼ぶ。- 名前空間を
/ks/v1のようにスラッシュ付きで書く。 - 書き込み系の
permission_callbackに__return_trueを指定して、誰でも書き込める状態にする。 - 404 が返ったとき、ルートの正規表現が一致していないのに、パーマリンクの問題と誤解する(
?rest_route=/ks/v1/...形式でも試すと切り分けられます)。 - コールバックで
echoやwp_die()を使い、JSON が壊れる。戻り値で返す。
REST API カスタムエンドポイントの作り方のチェックリスト
rest_api_initの中で登録したか。- 名前空間にバージョンを含めたか。
permission_callbackを、公開か権限チェックかで意図して書いたか。- 引数に
validate_callbackとsanitize_callbackを付けたか。 - 失敗時は
WP_Errorにステータスを入れたか。
REST API カスタムエンドポイントの作り方のFAQ(よくある質問)
Q. 既存の投稿タイプを REST に出すには?
A. register_post_type() の show_in_rest を true にします。独自エンドポイントは不要です。
Q. エンドポイントの一覧を確認する方法は?
A. /wp-json/ にアクセスすると、登録済みの名前空間とルートが見られます。
Q. admin-ajax.php との使い分けは?
A. 新規なら REST が扱いやすいです。既存の Ajax 資産の保守ではadmin-ajax.php が 0 や 400 を返す原因も参照してください。
筆者の見解(REST API カスタムエンドポイントの作り方)
REST のカスタムエンドポイントは、最初に「誰に許可するか」を permission_callback で決めてから、中身を書くのがよいと考えます。引数の検証を宣言的に書けるので、手動で $_POST を検査する Ajax より、抜け漏れを減らせます。一方で、小さな用途でも名前空間やバージョン管理が要るため、使い捨ての処理には少し大げさになる場面もあると思います。
REST API カスタムエンドポイントの作り方の関連項目
- register_rest_route() の使い方|説明・引数・注意点
- rest_api_init フックの使い方|説明・引数・注意点
- WP_REST_Request クラスの使い方|説明・引数・注意点
- WP_REST_Response クラスの使い方|説明・引数・注意点
- rest_ensure_response() の使い方|説明・引数・注意点
