Розробка кастомних REST API ендпоінтів WordPress — наша спеціалізація вже 5+ років. Мобільний додаток SPA-фронтенду вимагає від WordPress агрегованих даних з фільтрацією за таксономіями та метаполями. Стандартний /wp/v2/posts не вміє видавати суму замовлень клієнта за період або список проєктів із комбінованим сортуванням. Типова ситуація: без кешування при 5000 запитів на годину сервер падає. Розробники часто ліплять прямі SQL-запити, отримуючи N+1 проблем і вразливості. Наш досвід показав: правильно спроєктований кастомний ендпоінт вирішує ці задачі за 2–5 днів, знижуючи навантаження на базу в 3–5 разів. У цій статті розберемо типові сценарії, стек та архітектуру.
Щоб уникнути таких проблем, ми використовуємо єдиний інтерфейс для всіх даних: кастомний REST API ендпоінт агрегує записи, метаполя та таксономії за один запит. Це скорочує кількість HTTP-викликів у 5–10 разів і спрощує підтримку фронтенду. Наприклад, для одного з проєктів (каталог із 20 000 товарів) ми реалізували ендпоінт /my-plugin/v1/products з фільтрацією за категоріями, ціною та характеристиками — час відповіді скоротився з 4 до 1 секунди.
Обмеження стандартного REST API WordPress
Стандартні маршрути /wp/v2/posts та /wp/v2/pages хороші для читання записів, але не для:
- Агрегованих даних — сума замовлень клієнта за останній місяць.
- Складних фільтрів — комбінація метаполів і таксономій із сортуванням.
- Кастомних операцій — створення замовлення з перевіркою стоку та відправкою email.
Без кастомного ендпоінту клієнту доводиться робити кілька запитів або використовувати небезпечні SQL-запити. Кастомний ендпоінт з кешуванням дозволяє скоротити час відповіді з 4 до 1 секунди.
Реєстрація кастомного REST API ендпоінту
Реєстрація ендпоінту з GET та POST методами. Детальніше в REST API Handbook.
add_action('rest_api_init', function () { register_rest_route('my-plugin/v1', '/projects', [ [ 'methods' => WP_REST_Server::READABLE, '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, '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 ); } Який метод аутентифікації обрати?
Для GET-ендпоінтів достатньо публічного доступу. Для створення/зміни даних потрібна перевірка прав. Порівняємо методи:
| Метод | Сценарій | Складність |
|---|---|---|
| Cookie | Запити з адмінки | Нульова (вбудований) |
| Application Passwords | Зовнішні серверні клієнти | Низька (офіційний плагін) |
| JWT | SPA, мобільні додатки | Середня (плагін або самописний код) |
Приклад перехоплення Bearer-токена:
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; }); Кешування відповідей REST API
Для важких запитів використовуємо Transients API. Це знижує навантаження на БД у 3–5 разів. Приклад:
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; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_projects_%'"); }); Що входить у розробку ендпоінтів
| Етап | Результат |
|---|---|
| Аналітика | Визначення ендпоінтів, типів даних, методів аутентифікації |
| Проєктування | Схема маршрутів, структура відповідей, валідація параметрів |
| Реалізація | Написання коду, реєстрація роутів, обробники, кешування |
| Тестування | Модульні тести (PHPUnit), ручне тестування через curl |
| Деплой | Розгортання на бойовому сервері, налаштування моніторингу |
| Документація | OpenAPI-схема або інструкція для розробників |
Ми гарантуємо дотримання термінів і надаємо пост-релізну підтримку протягом 30 днів. Зв'яжіться з нами для оцінки вашого проєкту — ми проконсультуємо з архітектури та обсягу робіт. Замовте розробку кастомних ендпоінтів і скоротите час інтеграції вдвічі.
Порядок розробки кастомного REST API
- Аудит — визначаємо список ендпоінтів, методів (GET/POST/PUT/DELETE) та структури відповідей.
- Проєктування схеми маршрутів — версіонування (
/my-plugin/v1/), аргументи, валідація параметрів. - Реалізація обробників — написання callback-функцій, форматування даних, обробка помилок.
- Налаштування аутентифікації — Cookie (для адмінки), Application Passwords або JWT (для SPA/мобільних).
- Кешування — Transients API або Redis, інвалідація при зміні даних.
- Тестування через curl та PHPUnit, документація у OpenAPI-форматі.
Вартість розробки визначається після аналізу вашого проєкту. Це краще, ніж прямі SQL-запити з N+1 проблемами та вразливостями до SQL-ін'єкцій.
Як налагодити кастомний REST API WordPress?
Використовуйте curl -X GET https://site.com/wp-json/my-plugin/v1/projects -v для базової перевірки. Увімкніть WP_DEBUG та WP_DEBUG_LOG у wp-config.php — помилки PHP потраплять у debug.log. Плагін Query Monitor покаже всі SQL-запити, виконані під час виклику ендпоінту, і допоможе виявити N+1 проблеми. Перевірте заголовки відповіді: X-WP-Total має містити кількість записів, Content-Type: application/json. При помилці 401 переконайтеся, що permission_callback повертає true або коректно перевіряє права користувача.
Чому кастомні ендпоінти кращі за прямі SQL-запити?
Кастомний REST API ендпоінт забезпечує безпеку (фільтрація через WP API), кешування (Transients/Redis) та версіонування. За нашими даними, перехід на кастомні ендпоінти скорочує час на інтеграцію вдвічі та знижує кількість помилок на 60%. Приклади кастомних ендпоінтів: get_projects, create_order та ін. Більше 5 років ми розробляємо WordPress-рішення, запущено 30+ проєктів з кастомними REST API. Отримайте консультацію — оцінимо ваш проєкт і запропонуємо оптимальне рішення.







