2026年10月3日

2026年10月3日

WordPressでREST APIを使ってデータを取得する方法

はじめに

「WordPressのデータをJavaScriptで取得してSPAを作りたい」「外部サービスからWordPressの投稿を取得したい」——WordPress REST APIを使うと、WordPressをヘッドレスCMSとして活用したり、フロントエンドから動的にコンテンツを取得したりできます。

症状・原因

WordPress REST APIはバージョン4.7から標準搭載されています。/wp-json/wp/v2/postsで投稿一覧を取得できますが、認証が必要なエンドポイントや独自のカスタムエンドポイントの作り方が分かりにくいことがあります。

解決手順

ステップ1:標準エンドポイントでデータを取得する

# 投稿一覧を取得(パブリック)
curl https://example.com/wp-json/wp/v2/posts

# クエリパラメータで絞り込み
curl "https://example.com/wp-json/wp/v2/posts?per_page=5&page=1&status=publish&_embed"

# 特定の投稿を取得
curl https://example.com/wp-json/wp/v2/posts/123

# カテゴリー別に取得
curl "https://example.com/wp-json/wp/v2/posts?categories=5&per_page=10"

# カスタム投稿タイプを取得
curl https://example.com/wp-json/wp/v2/product

# ページ一覧
curl https://example.com/wp-json/wp/v2/pages

# メディア
curl https://example.com/wp-json/wp/v2/media

# ユーザー(公開情報のみ)
curl https://example.com/wp-json/wp/v2/users
// JavaScriptからREST APIを呼び出す(フロントエンド)
async function getPosts(page = 1, perPage = 10) {
    const base = wpApiSettings.root; // WordPress が wp_localize_script で提供
    const url  = `${base}wp/v2/posts?page=${page}&per_page=${perPage}&_embed`;

    const res = await fetch(url, {
        headers: {
            'X-WP-Nonce': wpApiSettings.nonce, // ログイン中ユーザーのNonce
        },
    });

    if (!res.ok) {
        const total      = res.headers.get('X-WP-Total');
        const totalPages = res.headers.get('X-WP-TotalPages');
        throw new Error(`HTTP ${res.status}`);
    }

    const total      = res.headers.get('X-WP-Total');
    const totalPages = res.headers.get('X-WP-TotalPages');
    const posts      = await res.json();

    return { posts, total: +total, totalPages: +totalPages };
}

// 使用例
getPosts(1, 5).then(({ posts, totalPages }) => {
    posts.forEach(post => {
        console.log(post.title.rendered, post.link);
    });
    console.log('総ページ数:', totalPages);
});

ステップ2:認証を設定する

// functions.php: Application Password(WordPress 5.6以降)
// 管理画面 → ユーザー → プロフィール → アプリケーションパスワード で生成

// Basic認証でAPIを呼び出す(サーバー側)
$response = wp_remote_get(
    'https://example.com/wp-json/wp/v2/posts?status=draft',
    [
        'headers' => [
            'Authorization' => 'Basic ' . base64_encode( 'username:application_password' ),
        ],
    ]
);

if ( ! is_wp_error( $response ) ) {
    $posts = json_decode( wp_remote_retrieve_body( $response ), true );
}
// JavaScriptからApplication Passwordで認証
const credentials = btoa('username:app_password'); // Application Password

const res = await fetch('https://example.com/wp-json/wp/v2/posts', {
    method: 'POST',
    headers: {
        'Authorization': `Basic ${credentials}`,
        'Content-Type':  'application/json',
    },
    body: JSON.stringify({
        title:   '新しい投稿タイトル',
        content: '本文テキスト',
        status:  'draft',
    }),
});

const newPost = await res.json();
console.log('作成した投稿ID:', newPost.id);

ステップ3:カスタムエンドポイントを作成する

// functions.php: カスタムエンドポイントを登録
add_action( 'rest_api_init', function() {

    // GET /wp-json/myplugin/v1/stats
    register_rest_route( 'myplugin/v1', '/stats', [
        'methods'             => WP_REST_Server::READABLE, // GET
        'callback'            => 'my_api_get_stats',
        'permission_callback' => '__return_true', // パブリック
    ] );

    // GET /wp-json/myplugin/v1/posts/(?P<id>\d+)/related
    register_rest_route( 'myplugin/v1', '/posts/(?P<id>\d+)/related', [
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => 'my_api_get_related',
        'permission_callback' => '__return_true',
        'args'                => [
            'id' => [
                'required'          => true,
                'validate_callback' => function( $param ) {
                    return is_numeric( $param );
                },
            ],
        ],
    ] );

    // POST /wp-json/myplugin/v1/contact(認証必須)
    register_rest_route( 'myplugin/v1', '/contact', [
        'methods'             => WP_REST_Server::CREATABLE, // POST
        'callback'            => 'my_api_post_contact',
        'permission_callback' => function() {
            return current_user_can( 'edit_posts' ); // ログイン + 権限チェック
        },
    ] );
} );

// コールバック関数
function my_api_get_stats( WP_REST_Request $request ) {
    return new WP_REST_Response( [
        'post_count'    => wp_count_posts()->publish,
        'comment_count' => wp_count_comments()->approved,
        'user_count'    => count_users()['total_users'],
    ], 200 );
}

function my_api_get_related( WP_REST_Request $request ) {
    $post_id  = (int) $request->get_param( 'id' );
    $post     = get_post( $post_id );

    if ( ! $post ) {
        return new WP_Error( 'not_found', '投稿が見つかりません', [ 'status' => 404 ] );
    }

    $tags     = wp_get_post_tags( $post_id, [ 'fields' => 'ids' ] );
    $related  = get_posts( [
        'tag__in'        => $tags,
        'post__not_in'   => [ $post_id ],
        'posts_per_page' => 5,
    ] );

    $data = array_map( function( $p ) {
        return [
            'id'      => $p->ID,
            'title'   => $p->post_title,
            'link'    => get_permalink( $p->ID ),
            'excerpt' => get_the_excerpt( $p ),
        ];
    }, $related );

    return new WP_REST_Response( $data, 200 );
}

ステップ4:カスタム投稿タイプをREST APIに対応させる

// functions.php: カスタム投稿タイプをREST APIに公開
register_post_type( 'product', [
    'label'        => '製品',
    'public'       => true,
    'show_in_rest' => true,        // REST APIに公開
    'rest_base'    => 'products',  // エンドポイント名 /wp-json/wp/v2/products
    'supports'     => [ 'title', 'editor', 'thumbnail', 'custom-fields' ],
] );

// 既存の投稿タイプをREST APIに追加公開
add_filter( 'register_post_type_args', function( $args, $post_type ) {
    if ( $post_type === 'portfolio' ) {
        $args['show_in_rest'] = true;
    }
    return $args;
}, 10, 2 );

// カスタムフィールドをREST APIレスポンスに追加
add_action( 'rest_api_init', function() {
    register_rest_field( 'post', 'view_count', [
        'get_callback' => function( $post ) {
            return (int) get_post_meta( $post['id'], '_view_count', true );
        },
        'schema' => [
            'description' => '閲覧数',
            'type'        => 'integer',
        ],
    ] );
} );

ステップ5:REST APIのセキュリティを強化する

// functions.php: REST APIへのアクセスを制限

// 未認証ユーザーがユーザー一覧を取得できないようにする
add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( ! is_user_logged_in() ) {
        if ( isset( $endpoints['/wp/v2/users'] ) ) {
            unset( $endpoints['/wp/v2/users'] );
        }
        if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
            unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
        }
    }
    return $endpoints;
} );

// 未認証ユーザーのREST APIアクセスを全面制限(必要な場合)
add_filter( 'rest_authentication_errors', function( $result ) {
    // 未認証ならエラーを返す(パブリックサイトでは通常使わない)
    if ( ! empty( $result ) ) return $result;

    if ( ! is_user_logged_in() ) {
        return new WP_Error(
            'rest_not_logged_in',
            'REST APIへのアクセスにはログインが必要です。',
            [ 'status' => 401 ]
        );
    }

    return $result;
} );

// レート制限: 同一IPからの連続アクセスを制限
add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) {
    $ip      = $_SERVER['REMOTE_ADDR'] ?? '';
    $key     = 'rest_rate_' . md5( $ip );
    $count   = (int) get_transient( $key );

    if ( $count > 100 ) { // 1分間に100リクエストまで
        return new WP_Error( 'rate_limit', 'リクエスト制限に達しました。', [ 'status' => 429 ] );
    }

    set_transient( $key, $count + 1, 60 );
    return $result;
}, 10, 3 );

注意事項

  • REST APIはデフォルトで/wp-json/以下で公開されます。パーマリンク設定が「デフォルト」の場合は/?rest_route=/形式になります。SEO的にはパーマリンクを設定することを推奨します。
  • カスタムエンドポイントのpermission_callbackを'__return_true'にするとパブリックになります。センシティブなデータを返す場合は必ず適切な権限チェックを実装してください。
  • Application Passwordはhttps環境でのみ有効です。ローカル開発環境ではdefine('WP_ENVIRONMENT_TYPE', 'local')を設定するか、Basic認証を.htaccessで個別に設定する必要があります。

まとめ

WordPress REST APIの活用は「標準エンドポイントでの基本取得→Application Passwordで認証→register_rest_routeでカスタムエンドポイント作成→show_in_restでカスタム投稿を公開→未認証制限・レート制限でセキュリティ強化」の流れで整備します。関連記事:WordPressでカスタム投稿タイプを作成する方法、WooCommerceを初期設定してECサイトを構築する方法。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲