ZATYのBLOG

お問い合わせする

WordPressでオリジナルのRest APIのエンドポイントを作る

WordPressではデフォルトでRest APIが用意されていますが、オリジナルのRest APIのエンドポイントを作成することで、独自の機能を織り交ぜた処理が可能になります。

今回は、「WordPressのRest APIを使ってカスタムフィールドの値を取得する」というサンプルとともにオリジナルのRest APIを作成します。

Rest APIやカスタムフィールドについて知らない方は、先にこの記事からご覧ください。

今回のサンプルAPIの仕様を先に決める

今回の投稿はイベント情報についての記事で、カスタムフィールドに、イベントの日時(メタキー“event_date”)と地域(メタキー“event_place”)が設定されているとします。

外部(https://blog.zaty.jpとする)からRest APIを叩き、新着の投稿情報が取得できることがゴールです。

取得するデータはJSON形式で、以下のようなデータになるようにすることが目的で、運用サイトでは既に投稿済みであると仮定します(内容は全てダミーです)。

[
  {
    "title": "WordPress講習会のお知らせ",
    "url": "https://example.com/1",
    "event_date": "2024-06-24",
    "event_place": "東京"
  },
  {
    "title": "WebGL勉強会の事前申し込み",
    "url": "https://example.com/2",
    "event_date": "2024-04-09",
    "event_place": "大阪"
  },
  {
    "title": "会員限定オフ会開催決定",
    "url": "https://example.com/3",
    "event_date": "2024-03-21",
    "event_place": "名古屋"
  }
]

今回はあくまでオリジナルのRest APIを作れるようにするための解説なので、機能としては最低限でありつつ網羅するように、記事数は、"post_count"というnameで数字をPOST送信することで記事数を指定できるようにします。

新しいエンドポイントを作成

WordPressでは、https://blog.zaty.jp/wp-json/以降のuri階層をRest APIとして使用しています。この階層は動的に作成されたページなので、ファイル自体は存在しません。

新しいRest APIのエンドポイントは、register_rest_route関数を利用することで作成できます。

<?php register_rest_route( $name_space, $route, $args = array(), $override = false ); ?>

$name_spaceは作成するRest APIが他のエンドポイントと被らないように設定します。プラグイン名やパッケージ名など、わかりやすく競合しない命名をしましょう。

$routeはURLの最後に繋がる文字列です。ここは正規表現と正規表現の命名を利用することで、Rest APIの機能部分でここの文字列を使用することができます。

$argsは実際にそのURLにリクエストがあった時の処理等の記述をまとめます。詳しい内容は後述します。

$overrideは同じエンドポイントが作成されていた場合、上書きするかどうかを設定します。初期設定では上書きしないfalseです。

第三引数の$argsについて

第三引数は、第一引数と第二引数で設定したエンドポイントにアクセスされた時の処理を設定します。

$args = array(
  'methods'             => 'POST', // 'GET'などの送信の種類
  'callback'            => 'callback_function', // 実際に実行される関数名
  'permission_callback' => 'permission_callback_function', // 実行関数が実行される前に確認する
  'args'                => array() // 送信されたパラメータの設定
);

この設定のためには、callback_function部分の関数とpermission_callback_function部分の関数を定義する必要があります。

 API表示の流れ 
  1. エンドポイントにmethodsで決めた送信でリクエスト
  2. permission_callbackで設定した関数が、trueかどうか確認
  3. callbackで設定した関数が実行

サンプルプログラム

仕様を見るだけではわかりにくいので、例を見て作成方法を理解しましょう。

add_action( 'rest_api_init', 'event_rest_api_end_point' );
function event_rest_api_end_point () {
  $post_args = array(
    'post_count'  =>  array(
      'type'     => 'interger',
      'required' => false,
      'default'  => 5
    )
  );
  register_rest_route( 
    'event',
    '/(?P<order>[a-z]+)',
    array(
      'methods'             => 'POST',
      'callback'            => 'event_rest_api_callback',
      'permission_callback' => 'event_permission_callback',
      'args'                => $post_args
    )
  );
}
  
function event_rest_api_callback ( $request ) {
  $data = $request->get_params();
  if ( 'new' === $data['order'] ) {
    $posts_per_page = $data['post_count'];
    $args = array(
      'post_type'      => 'post',
      'posts_per_page' => $posts_per_page,
      'orderby'        => 'date',
      'order'          => 'DESC'
    );
    $query = new WP_Query( $args );
    if ( $query->have_posts() ) {
      $posts = array();
      while ( $query->have_posts() ) {
        $query->the_post();
        $post = array(
          'title' => esc_html( get_the_title() ),
          'url'   => esc_url( get_the_permalink() )
        );
        $meta_date  = get_post_meta( get_the_ID(), 'event_date', true );
        $meta_place = get_post_meta( get_the_ID(), 'event_place', true );
        $post['event_date']  = esc_html( $meta_date ? $meta_date : '未指定' );
        $post['event_place'] = esc_html( $meta_place ? $meta_place : '未指定' );

        array_push( $posts, $post );
      }
      wp_reset_postdata();
    }
    
    return $posts;
  }else {
    return new WP_Error( 'invalid_order', 'orderの形式が不適当です。', array( 'status' => 400 ) );
  }
}
  
function event_permission_callback () {
  $is_permitted = 'https://blog.zaty.jp' === $request->get_header( 'origin' );
  return $is_permitted;
}

このプログラムをfunctions.phpに書くと、{ドメイン}/wp-json/event/new/というURLに、https://blog.zaty.jp/からPOST送信することで、新着記事のタイトル・投稿のURL・“event_date”のカスタムフィールドデータ・“event_place”のカスタムフィールドデータを取得することができます。

"post_count"パラメータで整数を送信することで取得する記事数を指定することができ、指定しなかった場合は5つの新着記事を取得します。

コード解説

上記したプログラムを仕組みが見やすいように今回必要な部分以外を省略して見通してみます。

省略部分は// ...と表します。

/** アクションフックで、エンドポイントを設定した関数を設定 */
add_action( 'rest_api_init', 'event_rest_api_end_point' );
function event_rest_api_end_point () {
  /** POST送信として受け入れるパラメータを設定 */
  $post_args = array(
    'post_count'  =>  array(
      // ...
    )
  );
  /** エンドポイントの設定 */
  register_rest_route( 
    'event',
    '/(?P<order>[a-z]+)',
    array(
      'methods'             => 'POST',
      'callback'            => 'event_rest_api_callback', // 下記の関数名を指定
      'permission_callback' => 'event_permission_callback', // 下記の関数名を指定
      'args'                => $post_args
    )
  );
}
 
/** エンドポイントのURLにアクセスされた時に実行される関数 */
function event_rest_api_callback ( $request ) {
  $data = $request->get_params();
  /** エンドポイントのルート部分に指定した文字列がnewの時正しく動かす */
  if ( 'new' === $data['order'] ) {
    // ...
    /** 戻り値の配列がRest APIで返すデータになる */
    return $posts;
  }else {
    /** 指定外の文字列の時はエラーを返す */
    return new WP_Error( 'invalid_order', 'orderの形式が不適当です。', array( 'status' => 400 ) );
  }
}
  
function event_permission_callback () {
  // ...
  /** 戻り値がtrueの時event_rest_api_callbackが実行される */
  return $is_permitted;
}
  1. register_rest_route関数で指定したURLにPOST送信される
  2. 設定したPOSTパラメータが正しいか確認
  3. event_permission_callback関数がtrueを戻すか確認
  4. "permission_callback"がtrueだった時、event_rest_api_callback関数が動く

という処理の流れでプログラムが動きます。

register_rest_route関数用の関数

event_rest_api_end_pointという関数を作成し、その中でregister_rest_route関数を定義します。

add_action( 'rest_api_init', 'event_rest_api_end_point' );
function event_rest_api_end_point () {
  $post_args = array(
    'post_count'  =>  array(
      'type'     => 'interger',
      'required' => false,
      'default'  => 5
    )
  );
  register_rest_route( 
    'event',
    '/(?P<order>[a-z]+)',
    array(
      'methods'             => 'POST',
      'callback'            => 'event_rest_api_callback',
      'permission_callback' => 'event_permission_callback',
      'args'                => $post_args
    )
  );
}

まず、$post_argsという変数を作成し、POST送信で求めるパラメータの指定をします。配列の1つ目、'post_count'の部分が受け付けるパラメータ名です。このパラメータを連想配列としてさらに配列を指定します。

'type'がパラメータ値の型、'required'がそのパラメータを必須にするかどうかを指定します。型と違うパラメータを送ったり必須なパラメータを送らなかったりするとリクエストを受け付けた直後にエラーを返します。今回は、'required'false、任意にしたので、'post_count'パラメータのデフォルト値を設定しています。今回はデフォルト値を5にしました。

このようにPOST、GET送信で受け付けるパラメータを指定することで、セキュリティが上昇します。

次に、register_rest_route関数の第二引数に注目してください。ここでは、エンドポイントの最後のURI部分の文字列を正規表現で指定します。

'/(P<order>[a-z]+)'P<order>の部分は後に続く正規表現の部分に命名する記述で、[a-z]+は1文字以上の小文字のアルファベットを指定する正規表現です。正規表現部分に命名することで、コールバック関数内で正規表現部分の文字列を再利用することができるようになります。

例えば、/event/hogeというURIにリクエストがあった時、"hoge"の部分が"order"をキーとするリクエストとして扱われます。また、/event/hoge1/event/hoge-fuga/event/<script>hoge</script>のような指定外の文字列が含まれている場合は、404エラーになります。

permission_callbackに対応する関数

register_rest_route関数の第三引数に指定している連想配列'permission_callback'に指定する関数名について解説します。

今回は、event_permission_callbackという関数を作成して指定しました。

function event_permission_callback( $request ) {
  $is_permitted = 'https://blog.zaty.jp' === $request->get_header( 'origin' );

  return $is_permitted;
}

この関数では、HTTPリクエストの発信元が'https://blog.zaty.jp'からであるかを確認し、真偽を戻り値として返します(ここではヘッダ偽装については触れません)。

$requestは、URLへのリクエストの情報が入ったオブジェクトで、get_header()メソッドを使って発信元と'https://blog.zaty.jp'と比較します。

戻り値が偽の時は、リクエストの権限がないとして401エラーを返します。

このようにpermission_callbackに指定する関数によってRest APIの使用権限を追加することができます。permission_callbackは指定しなくてもプログラムは動きますが、WordPressのバージョン5.5から公開されたRest APIには、permission_callbackを付与することが推奨されています。

もし、全ての人に公開して問題ないAPIの場合、WordPress関数である、__return_trueという関数を使います。

register_rest_route(
  'myapi/v1', '/route/',
  array(
    'methods'             => 'GET',
    'callback'            => 'callback_func',
    'permission_callback' => '__return_true'
  )
);

__return_true関数は常にtrueを返す関数です。指定しなくても挙動が変わらないとしても明示的に'permission_callback'パラメータを設定するようにしましょう。

callbackに対応する関数

APIにリクエストを送って、実際に機能として働く部分がcallbackに指定した関数です。

function event_rest_api_callback ( $request ) {
  $data = $request->get_params();
  if ( 'new' === $data['order'] ) {
    $posts_per_page = $data['post_count'];
    $args = array(
      'post_type'      => 'post',
      'posts_per_page' => $posts_per_page,
      'orderby'        => 'date',
      'order'          => 'DESC'
    );
    $query = new WP_Query( $args );
    if ( $query->have_posts() ) {
      $posts = array();
      while ( $query->have_posts() ) {
        $query->the_post();
        $post = array(
          'title' => esc_html( get_the_title() ),
          'url'   => esc_url( get_the_permalink() )
        );
        $meta_date  = get_post_meta( get_the_ID(), 'event_date', true );
        $meta_place = get_post_meta( get_the_ID(), 'event_place', true );
        $post['event_date']  = esc_html( $meta_date ? $meta_date : '未指定' );
        $post['event_place'] = esc_html( $meta_place ? $meta_place : '未指定' );

        array_push( $posts, $post );
      }
      wp_reset_postdata();
    }
    
    return $posts;
  }else {
    return new WP_Error( 'invalid_order', 'orderの形式が不適当です。', array( 'status' => 400 ) );
  }
}

引数に割り当てる$requestは、リクエストに関係する情報が入ったオブジェクトが入っています。get_params()メソッドを使うことで送信されたパラメータなどを取得できます。

このパラメータには、POST送信された内容とURLに含まれていた(今回は'order')正規表現の部分が格納されます。コード3行目と4行目を見ると、'order'は'new'が含まれていることを想定しており、'post_count'はPOST送信された整数を想定していることがわかります。

正しくPOST送信がされていることが確認できたら、WP_Queryクラスを使って記事を取得し、$posts変数配列に格納していきます。

register_rest_routeのコールバックで配列を戻り値にすることで、自動的にJSON形式に変換しレスポンスとして表示してくれます。

まとめ

今回は、WordPress Rest APIのオリジナルのエンドポイントを作る方法を解説しました。

オリジナルのRest APIは独自の機能やレスポンスを実装することができます。昨今話題のヘッドレスWordPressや、WEBサイトの一部分でWordPressのデータを利用する際にとても便利な機能になります。

一方、セキュリティ面では注意が必要です。今回サンプルとして作成した、カスタムフィールドのデータをレスポンスするAPIではデータの取得ですが、システムによっては書き込みや更新が必要になるAPIもあります。

特にデータベースを操作するRest APIを作成する際は、より受け取るデータの扱いやリクエストの権限に注意しましょう。