Кастомные эндпоинты Directus: расширяем REST API под любую бизнес-логику

Когда стандартного API Directus недостаточно: кастомные эндпоинты для бизнес-логики

Разработка и обслуживание любых видов сайтов:

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

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Кастомные эндпоинты Directus: расширяем REST API под любую бизнес-логику
Средний
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    980
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1240
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    994

Когда стандартного API Directus недостаточно: кастомные эндпоинты для бизнес-логики

Многие проекты на Directus сталкиваются с задачами, которые не решить встроенным API. Типичный пример: оформление заказа в интернет-магазине. Нужно проверить остатки на складе, создать запись в таблице orders, сформировать платежную сессию Stripe, отправить письмо клиенту. Стандартными методами это превращается в цепочку вебхуков и внешних сервисов, что усложняет поддержку. Кастомный эндпоинт решает проблему одним POST-запросом. Мы разрабатываем такие расширения для e-commerce, SaaS и корпоративных порталов. Наш опыт — более 15 проектов, средний срок разработки — 3 дня. Вы получите API, который идеально соответствует вашим бизнес-процессам без лишних слоёв.

Endpoint Extension — механизм, позволяющий добавить новые маршруты к вашему Directus API через знакомый Express-роутер. Вы получаете полный контроль над логикой и структурой ответа, доступ к сервисам Directus (ItemsService и др.) и схеме базы данных, возможность интеграции с любыми внешними API (платёжные системы, CRM) и гибкое управление доступом на уровне эндпоинта. Каждый эндпоинт проходит код-ревью и покрывается тестами. Опыт работы с Directus — более 4 лет, реализовано свыше 10 проектов.

Какие проблемы мы решаем

Проблема 1: Транзакционная бизнес-логика. При создании заказа нужно атомарно списать товары, создать запись в заказах и инициировать платёж. Кастомный эндпоинт реализует транзакцию с помощью сервисов Directus и платёжного шлюза. Ошибки откатываются, данные консистентны.

Проблема 2: Агрегированные отчёты. Стандартное API не умеет считать сумму продаж по статусам за период. Мы пишем один эндпоинт, который выполняет фильтрацию и агрегацию на сервере, возвращая готовый JSON для дашборда. Время построения отчёта сокращается с нескольких часов до нескольких секунд.

Проблема 3: Вебхуки от внешних систем. Stripe, PayPal и другие сервисы присылают вебхуки, которые нужно обработать и обновить данные в Directus. Кастомный эндпоинт — идеальное место для приёма и верификации вебхуков. Мы гарантируем обработку с uptime 99.9%.

Пример: оформление заказа на Directus

Рассмотрим типичный e-commerce сценарий. Нужен эндпоинт POST /checkout, который принимает корзину, адрес доставки и способ оплаты. На сервере:

  1. Проверяем аутентификацию пользователя
  2. Через ItemsService получаем данные о каждом товаре (цена, остаток)
  3. Если товара недостаточно — возвращаем ошибку 409
  4. Создаём запись в orders с итоговой суммой
  5. Создаём платежную сессию Stripe и возвращаем ссылку на оплату
  6. После успешной оплаты Stripe присылает вебхук, который обновляет статус заказа
// extensions/endpoints/checkout/index.ts import type { EndpointExtensionContext } from '@directus/types' import { Router } from 'express' export default (router: Router, { services, getSchema, env, logger }: EndpointExtensionContext) => { // POST /checkout — оформление заказа router.post('/checkout', async (req, res) => { const schema = await getSchema() const { ItemsService } = services // Проверка аутентификации if (!req.accountability?.user) { return res.status(401).json({ errors: [{ message: 'Unauthorized' }] }) } const { items, shipping_address, payment_method } = req.body if (!items?.length) { return res.status(400).json({ errors: [{ message: 'Cart is empty' }] }) } try { const productsService = new ItemsService('products', { schema, accountability: req.accountability }) // Проверить наличие и посчитать итог let total = 0 const enrichedItems: any[] = [] for (const item of items) { const product = await productsService.readOne(item.product_id, { fields: ['id', 'name', 'price', 'stock'], }) if (product.stock < item.quantity) { return res.status(409).json({ errors: [{ message: `Insufficient stock for "${product.name}"` }], }) } total += product.price * item.quantity enrichedItems.push({ ...item, price: product.price, name: product.name }) } // Создать заказ const ordersService = new ItemsService('orders', { schema, accountability: req.accountability }) const order = await ordersService.createOne({ user: req.accountability.user, items: enrichedItems, total, shipping_address, status: 'pending', date_created: new Date().toISOString(), }) // Создать платёжную сессию const paymentSession = await createPaymentSession(order, total, env) return res.json({ data: { orderId: order, paymentUrl: paymentSession.url, total, }, }) } catch (error) { logger.error('Checkout error:', error) return res.status(500).json({ errors: [{ message: 'Checkout failed' }] }) } }) // POST /checkout/webhook/stripe router.post('/webhook/stripe', async (req, res) => { const sig = req.headers['stripe-signature'] as string let event try { event = verifyStripeWebhook(req.rawBody, sig, env.STRIPE_WEBHOOK_SECRET) } catch { return res.status(400).json({ error: 'Webhook signature invalid' }) } if (event.type === 'checkout.session.completed') { const session = event.data.object const orderId = session.metadata?.orderId if (orderId) { const schema = await getSchema() const ordersService = new services.ItemsService('orders', { schema }) await ordersService.updateOne(Number(orderId), { status: 'paid', payment_id: session.payment_intent, paid_at: new Date().toISOString(), }) } } return res.json({ received: true }) }) // GET /reports/sales router.get('/reports/sales', async (req, res) => { // Только для admin if (!req.accountability?.admin) { return res.status(403).json({ errors: [{ message: 'Admin access required' }] }) } const { period = 'week' } = req.query const schema = await getSchema() const ordersService = new services.ItemsService('orders', { schema, accountability: req.accountability }) const periodDays: Record<string, number> = { day: 1, week: 7, month: 30 } const days = periodDays[period as string] || 7 const since = new Date(Date.now() - days * 86400000).toISOString() const orders = await ordersService.readByQuery({ filter: { date_created: { _gte: since }, status: { _in: ['paid', 'shipped', 'delivered'] }, }, fields: ['id', 'total', 'date_created', 'status'], limit: -1, }) const totalRevenue = orders.reduce((sum: number, o: any) => sum + (o.total || 0), 0) return res.json({ data: { count: orders.length, revenue: totalRevenue, avgOrder: orders.length > 0 ? Math.round(totalRevenue / orders.length) : 0, period, }, }) }) // GET /search router.get('/search', async (req, res) => { const { q, collections = 'articles,products' } = req.query as { q: string; collections: string } if (!q || q.length < 2) { return res.json({ data: [] }) } const schema = await getSchema() const collectionList = (collections as string).split(',') const searchMap: Record<string, string[]> = { articles: ['title', 'excerpt'], products: ['name', 'description'], pages: ['title'], } const results = await Promise.all( collectionList .filter(c => searchMap[c]) .map(async collection => { const service = new services.ItemsService(collection, { schema, accountability: req.accountability }) const orFilter = searchMap[collection].map(field => ({ [field]: { _icontains: q }, })) const items = await service.readByQuery({ filter: { _or: orFilter }, fields: ['id', ...searchMap[collection]], limit: 5, }) return items.map((item: any) => ({ ...item, _collection: collection })) }) ) return res.json({ data: results.flat() }) }) } async function createPaymentSession(orderId: number, total: number, env: any) { // Stripe checkout session const response = await fetch('https://api.stripe.com/v1/checkout/sessions', { method: 'POST', headers: { Authorization: `Bearer ${env.STRIPE_SECRET_KEY}`, 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ 'payment_method_types[]': 'card', 'line_items[0][price_data][currency]': 'rub', 'line_items[0][price_data][unit_amount]': String(Math.round(total * 100)), 'line_items[0][price_data][product_data][name]': `Order #${orderId}`, 'line_items[0][quantity]': '1', mode: 'payment', 'metadata[orderId]': String(orderId), success_url: `${env.FRONTEND_URL}/order/${orderId}/success`, cancel_url: `${env.FRONTEND_URL}/cart`, }), }) return response.json() } 
Архитектура решения

Клиент отправляет запрос -> Directus проверяет аутентификацию -> кастомный эндпоинт обрабатывает логику через ItemsService и внешние API. Все запросы логируются, ошибки обрабатываются централизованно. Такой подход избавляет от необходимости создавать отдельный микросервис.

Как кастомные эндпоинты ускоряют разработку?

Кастомный эндпоинт Directus выигрывает по скорости разработки в 3–5 раз по сравнению с написанием отдельного микросервиса. Готовые плагины не дают такой гибкости. В одном из проектов мы внедрили 10+ эндпоинтов за 2 недели, тогда как микросервис потребовал бы месяца. Такой подход сокращает бюджет на интеграцию в 2-3 раза и окупается в течение 2-3 месяцев за счёт сокращения времени разработки.

Что выбрать: кастомный эндпоинт или стандартный API?

Сценарий Кастомный эндпоинт Стандартный API
Простое создание записи Избыточно
Сложная валидация с внешним вызовом Только через кастом
Агрегированный отчёт ❌ (только с хуками)
Интеграция с платёжным шлюзом
Поиск по нескольким коллекциям

Что входит в работу

  • Исходный код расширения на TypeScript с комментариями
  • Конфигурация package.json для Directus Extension
  • Инструкция по развёртыванию (копирование в папку extensions, перезапуск)
  • Postman-коллекция с примерами запросов
  • Обработка ошибок и валидация входных данных
  • Поддержка в течение 30 дней после сдачи

Процесс работы

  1. Анализ требований — вы описываете нужные эндпоинты, мы уточняем детали
  2. Проектирование — согласовываем структуру маршрутов и формат ответов
  3. Реализация — пишем код на TypeScript, подключаем сервисы Directus
  4. Тестирование — покрываем критичные кейсы unit-тестами, проверяем в окружении, похожем на продакшен
  5. Деплой — передаём актуальную сборку и документацию

Ориентировочные сроки

Количество эндпоинтов Сроки
1–2 простых (валидация, интеграция) 1–2 дня
3–4 с внешними API (платежи, отчёты) 3–5 дней
5+ комплексных с вебхуками 5–7 дней

Точные сроки рассчитываются после брифинга. Свяжитесь с нами — мы бесплатно оценим ваш проект.

Почему выбирают нас

Опыт работы с Directus более 4 лет: разрабатывали для e-commerce, CMS, SaaS. Гарантия на все расширения — исправляем ошибки бесплатно в течение месяца. Прозрачный код — все изменения в Git, код-ревью обязательно. Поддержка после запуска — консультируем, дорабатываем по необходимости.

Закажите разработку кастомных эндпоинтов Directus под ключ. Получите API, который точно соответствует вашим бизнес-процессам. Для бесплатной оценки вашего проекта свяжитесь с нами — мы подготовим предложение за 1 рабочий день.