Разработка кастомных REST API эндпоинтов WordPress

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка кастомных REST API эндпоинтов WordPress
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка кастомных REST API эндпоинтов WordPress

WordPress REST API существует с версии 4.4 и покрывает стандартные операции с записями, таксономиями, пользователями. Но как только нужен нестандартный запрос — агрегированные данные, бизнес-логика, интеграция со сторонним сервисом — требуется кастомный эндпоинт. Разработка набора эндпоинтов с аутентификацией и валидацией занимает от 2 до 5 дней.

Регистрация эндпоинта

add_action('rest_api_init', function () {
    register_rest_route('my-plugin/v1', '/projects', [
        [
            'methods'             => WP_REST_Server::READABLE, // GET
            'callback'            => 'my_plugin_get_projects',
            'permission_callback' => '__return_true', // публичный
            'args'                => [
                'category' => [
                    'type'              => 'string',
                    'sanitize_callback' => 'sanitize_title',
                ],
                'tech'     => [
                    'type'              => 'array',
                    'items'             => ['type' => 'string'],
                    'sanitize_callback' => function ($value) {
                        return array_map('sanitize_title', (array) $value);
                    },
                ],
                'per_page' => [
                    'type'              => 'integer',
                    'default'           => 12,
                    'minimum'           => 1,
                    'maximum'           => 100,
                    'sanitize_callback' => 'absint',
                ],
                'page'     => [
                    'type'              => 'integer',
                    'default'           => 1,
                    'minimum'           => 1,
                    'sanitize_callback' => 'absint',
                ],
            ],
        ],
        [
            'methods'             => WP_REST_Server::CREATABLE, // POST
            'callback'            => 'my_plugin_create_project',
            'permission_callback' => function () {
                return current_user_can('edit_posts');
            },
        ],
    ]);

    // Эндпоинт с параметром в пути
    register_rest_route('my-plugin/v1', '/projects/(?P<id>\d+)', [
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => 'my_plugin_get_project',
        'permission_callback' => '__return_true',
        'args'                => [
            'id' => [
                'validate_callback' => function ($param) {
                    return is_numeric($param) && $param > 0;
                },
            ],
        ],
    ]);
});

Обработчик GET-запроса

function my_plugin_get_projects(WP_REST_Request $request): WP_REST_Response|WP_Error {
    $per_page = $request->get_param('per_page');
    $page     = $request->get_param('page');
    $category = $request->get_param('category');
    $techs    = $request->get_param('tech');

    $query_args = [
        'post_type'      => 'project',
        'post_status'    => 'publish',
        'posts_per_page' => $per_page,
        'paged'          => $page,
        'no_found_rows'  => false,
    ];

    $tax_queries = [];

    if ($category) {
        $tax_queries[] = [
            'taxonomy' => 'project_category',
            'field'    => 'slug',
            'terms'    => $category,
        ];
    }

    if (!empty($techs)) {
        $tax_queries[] = [
            'taxonomy' => 'tech_stack',
            'field'    => 'slug',
            'terms'    => $techs,
            'operator' => 'IN',
        ];
    }

    if (!empty($tax_queries)) {
        $query_args['tax_query'] = array_merge(['relation' => 'AND'], $tax_queries);
    }

    $query    = new WP_Query($query_args);
    $projects = [];

    foreach ($query->posts as $post) {
        $projects[] = my_plugin_format_project($post);
    }

    $response = new WP_REST_Response($projects, 200);
    $response->header('X-WP-Total',      $query->found_posts);
    $response->header('X-WP-TotalPages', $query->max_num_pages);

    return $response;
}

function my_plugin_format_project(WP_Post $post): array {
    $thumbnail_id  = get_post_thumbnail_id($post->ID);
    $thumbnail_url = $thumbnail_id
        ? wp_get_attachment_image_url($thumbnail_id, 'large')
        : null;

    return [
        'id'          => $post->ID,
        'slug'        => $post->post_name,
        'title'       => wp_strip_all_tags($post->post_title),
        'excerpt'     => wp_strip_all_tags(get_the_excerpt($post)),
        'url'         => get_permalink($post->ID),
        'thumbnail'   => $thumbnail_url,
        'client'      => get_post_meta($post->ID, 'project_client', true),
        'year'        => (int) get_post_meta($post->ID, 'project_year', true),
        'categories'  => wp_get_post_terms($post->ID, 'project_category', ['fields' => 'slugs']),
        'tech_stack'  => wp_get_post_terms($post->ID, 'tech_stack', ['fields' => 'slugs']),
        'modified'    => get_post_modified_time('c', true, $post),
    ];
}

Обработчик POST-запроса с валидацией

function my_plugin_create_project(WP_REST_Request $request): WP_REST_Response|WP_Error {
    $body = $request->get_json_params();

    if (empty($body['title'])) {
        return new WP_Error('missing_title', 'Заголовок обязателен', ['status' => 422]);
    }

    $post_id = wp_insert_post([
        'post_type'    => 'project',
        'post_title'   => sanitize_text_field($body['title']),
        'post_content' => wp_kses_post($body['content'] ?? ''),
        'post_status'  => 'draft',
        'post_author'  => get_current_user_id(),
    ], true);

    if (is_wp_error($post_id)) {
        return new WP_Error('insert_failed', $post_id->get_error_message(), ['status' => 500]);
    }

    if (!empty($body['client'])) {
        update_post_meta($post_id, 'project_client', sanitize_text_field($body['client']));
    }

    return new WP_REST_Response(
        ['id' => $post_id, 'url' => get_permalink($post_id)],
        201
    );
}

Аутентификация

Стандартный WordPress REST API использует cookie-аутентификацию (для /wp-admin) и Application Passwords (для внешних клиентов). Для SPA или мобильного приложения — JWT через плагин или собственную реализацию:

add_filter('rest_authentication_errors', function ($result) {
    if (!empty($result)) return $result;

    $auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    if (!str_starts_with($auth_header, 'Bearer ')) {
        return $result; // пропускаем — другие методы аутентификации
    }

    $token = substr($auth_header, 7);
    $user_id = my_plugin_validate_jwt($token);

    if (is_wp_error($user_id)) {
        return $user_id;
    }

    wp_set_current_user($user_id);
    return true;
});

Кеширование ответов

Для публичных эндпоинтов с тяжёлыми запросами — Transients API:

function my_plugin_get_projects(WP_REST_Request $request): WP_REST_Response {
    $cache_key = 'projects_' . md5(serialize($request->get_params()));
    $cached    = get_transient($cache_key);

    if ($cached !== false) {
        $response = new WP_REST_Response($cached['data'], 200);
        $response->header('X-WP-Total', $cached['total']);
        $response->header('X-Cache', 'HIT');
        return $response;
    }

    // ... основная логика ...

    set_transient($cache_key, ['data' => $projects, 'total' => $total], 5 * MINUTE_IN_SECONDS);
    return $response;
}

Инвалидация при изменении записи:

add_action('save_post_project', function (int $post_id): void {
    global $wpdb;
    // Удаляем все transients, связанные с проектами
    $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_projects_%'");
});

Версионирование

Namespace my-plugin/v1 обязателен — он позволяет в будущем добавить v2 с breaking changes, не ломая существующих клиентов. Документирование ответов — через OpenAPI-схему или хотя бы через описание rest_api_init в args.