Розробка REST API для сайту на 1С-Бітрікс
Бітрікс має вбудований REST API для Бітрікс24, але для сайту на «1С-Бітрікс: Управління сайтом» власного REST немає — його потрібно будувати. Завдання виникає регулярно: мобільний додаток вимагає дані каталогу, сторонній сервіс хоче отримувати замовлення, фронтенд на React або Vue потрібно постачати даними без перезавантаження сторінки. Розберемо, як побудувати API на D7, що витримає навантаження та не перетвориться на смітник через півроку.
Архітектура: Controller + Router
Основа API — контролери на базі \Bitrix\Main\Engine\Controller. Кожен контролер відповідає за один ресурс:
/local/modules/my.api/lib/
├── Controllers/
│ ├── ProductController.php → GET /api/v1/products
│ ├── OrderController.php → GET/POST /api/v1/orders
│ ├── CategoryController.php → GET /api/v1/categories
│ └── AuthController.php → POST /api/v1/auth/token
├── Services/
│ ├── ProductService.php
│ └── OrderService.php
├── Transformers/
│ ├── ProductTransformer.php → форматування відповіді
│ └── OrderTransformer.php
└── Middleware/
├── AuthMiddleware.php
└── RateLimitMiddleware.php
Реалізація контролера
namespace MyApi\Controllers;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;
use MyApi\Services\ProductService;
use MyApi\Middleware\AuthMiddleware;
class ProductController extends Controller
{
public function configureActions(): array
{
return [
'list' => ['prefilters' => [new AuthMiddleware()]],
'detail' => ['prefilters' => [new AuthMiddleware()]],
'create' => ['prefilters' => [new AuthMiddleware(), new ActionFilter\HttpMethod(['POST'])]],
];
}
public function listAction(int $page = 1, int $perPage = 20, string $category = ''): array
{
$service = new ProductService();
$result = $service->getList($page, $perPage, $category);
return [
'data' => $result['items'],
'meta' => [
'total' => $result['total'],
'page' => $page,
'per_page' => $perPage,
'pages' => ceil($result['total'] / $perPage),
],
];
}
public function detailAction(int $id): array
{
$service = new ProductService();
$product = $service->getById($id);
if (!$product) {
$this->addError(new \Bitrix\Main\Error('Товар не знайдено', 404));
return [];
}
return ['data' => $product];
}
}
Аутентифікація
API-ключі — найпростіший варіант для server-to-server інтеграцій:
namespace MyApi\Middleware;
use Bitrix\Main\Engine\ActionFilter\Base;
use Bitrix\Main\Event;
use Bitrix\Main\EventResult;
class AuthMiddleware extends Base
{
public function onBeforeAction(Event $event): ?EventResult
{
$request = \Bitrix\Main\Application::getInstance()->getContext()->getRequest();
$apiKey = $request->getHeader('X-Api-Key');
$validKey = \Bitrix\Main\Config\Option::get('my.api', 'api_key');
if ($apiKey !== $validKey) {
$this->addError(new \Bitrix\Main\Error('Unauthorized', 401));
return new EventResult(EventResult::ERROR);
}
return null;
}
}
JWT — для користувацьких запитів (mobile app, SPA). Бібліотека firebase/php-jwt через Composer у /local/:
$decoded = \Firebase\JWT\JWT::decode(
substr($authHeader, 7), // прибираємо 'Bearer '
new \Firebase\JWT\Key($secret, 'HS256')
);
$userId = (int)$decoded->sub;
Токени зберігаються на клієнті. Refresh-токени зберігаються в b_option або в кастомній ORM-таблиці з прив'язкою до користувача.
Формат відповідей
Консистентний формат — запорука передбачуваної поведінки API:
// Успішна відповідь
{
"status": "ok",
"data": { ... },
"meta": { "total": 150, "page": 1 }
}
// Помилка
{
"status": "error",
"errors": [
{ "code": "NOT_FOUND", "message": "Товар не знайдено" }
]
}
Бітрікс-контролер формує відповідь автоматично, але формат за замовчуванням специфічний. Для повного контролю — перевизначаємо processAfterAction:
protected function processAfterAction(Action $action, $result)
{
$response = \Bitrix\Main\Application::getInstance()->getContext()->getResponse();
$response->addHeader('Content-Type', 'application/json; charset=utf-8');
if ($this->getErrors()) {
echo json_encode([
'status' => 'error',
'errors' => array_map(fn($e) => [
'code' => $e->getCode(),
'message' => $e->getMessage(),
], $this->getErrors()),
], JSON_UNESCAPED_UNICODE);
} else {
echo json_encode([
'status' => 'ok',
'data' => $result,
], JSON_UNESCAPED_UNICODE);
}
exit;
}
CORS
Для API, який викликається з іншого домену:
// На початку контролера або в окремому middleware
$response = \Bitrix\Main\Application::getInstance()->getContext()->getResponse();
$response->addHeader('Access-Control-Allow-Origin', 'https://app.example.com');
$response->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Api-Key');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
Документування: OpenAPI / Swagger
API без документації — це API тільки для того, хто його написав. Опис ендпоінтів у форматі OpenAPI 3.0 створюється вручну або генерується з анотацій (через бібліотеки типу zircote/swagger-php). Swagger UI розгортається як статична сторінка у /local/swagger/.
Обмеження запитів (Rate Limiting)
Лічильник запитів на основі Redis або через b_option (для малого навантаження):
$key = 'api_ratelimit_' . md5($apiKey);
$count = (int)\Bitrix\Main\Data\Cache::createInstance()->get($key);
if ($count > 1000) { // 1000 запитів на годину
http_response_code(429);
exit(json_encode(['error' => 'Rate limit exceeded']));
}
// інкремент через Redis або кастомний лічильник
Терміни
| Завдання | Термін |
|---|---|
| Базовий REST API (3–5 ресурсів, API-ключ, JSON-відповіді) | 1.5–2 тижні |
| API з JWT-аутентифікацією, правами користувачів, документацією | 3–5 тижнів |
| Повноцінний API з версіонуванням, rate limiting, тестами, CI | 6–10 тижнів |
REST API на Бітрікс будується зі стандартних блоків — контролери, ORM, кеш. Складність не в технології, а в проектуванні: правильні ендпоінти, консистентні формати відповідей, обробка граничних випадків. Погано спроектований API стає тягарем, що тягне вниз обидві сторони інтеграції.







