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

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка кастомных эндпоинтов API Payload CMS
Средняя
~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

Кастомные API эндпоинты Payload CMS

Payload автоматически генерирует REST API и GraphQL для всех коллекций. Кастомные эндпоинты нужны для бизнес-операций, которые не укладываются в стандартный CRUD: оформление заказа, интеграция с платёжной системой, вебхуки от внешних сервисов.

Эндпоинты на уровне коллекции

// collections/Orders.ts
import type { CollectionConfig, PayloadRequest } from 'payload/types'
import { Response } from 'express'

const Orders: CollectionConfig = {
  slug: 'orders',
  endpoints: [
    // POST /api/orders/checkout
    {
      path: '/checkout',
      method: 'post',
      handler: async (req: PayloadRequest, res: Response) => {
        const { items, customerEmail, shippingAddress } = req.body

        // Валидация
        if (!items?.length) {
          return res.status(400).json({ error: 'Items required' })
        }

        // Подсчёт итога
        let total = 0
        const enrichedItems = await Promise.all(
          items.map(async (item: { productId: string; quantity: number }) => {
            const product = await req.payload.findByID({
              collection: 'products',
              id: item.productId,
            })
            total += product.price * item.quantity
            return {
              product: item.productId,
              quantity: item.quantity,
              price: product.price,
              name: product.name,
            }
          })
        )

        // Создать заказ
        const order = await req.payload.create({
          collection: 'orders',
          data: {
            items: enrichedItems,
            total,
            customerEmail,
            shippingAddress,
            status: 'pending',
          },
          req,
        })

        // Создать платёжную сессию
        const paymentSession = await stripeClient.checkout.sessions.create({
          payment_method_types: ['card'],
          line_items: enrichedItems.map(item => ({
            price_data: {
              currency: 'rub',
              product_data: { name: item.name },
              unit_amount: Math.round(item.price * 100),
            },
            quantity: item.quantity,
          })),
          mode: 'payment',
          success_url: `${process.env.FRONTEND_URL}/order/${order.id}/success`,
          cancel_url: `${process.env.FRONTEND_URL}/cart`,
          metadata: { orderId: String(order.id) },
        })

        return res.json({
          orderId: order.id,
          paymentUrl: paymentSession.url,
        })
      },
    },

    // POST /api/orders/webhook/stripe
    {
      path: '/webhook/stripe',
      method: 'post',
      handler: async (req: PayloadRequest, res: Response) => {
        const sig = req.headers['stripe-signature'] as string

        let event
        try {
          event = stripe.webhooks.constructEvent(
            req.rawBody,
            sig,
            process.env.STRIPE_WEBHOOK_SECRET!
          )
        } catch (err) {
          return res.status(400).json({ error: 'Webhook signature verification failed' })
        }

        if (event.type === 'checkout.session.completed') {
          const session = event.data.object as Stripe.Checkout.Session
          const orderId = session.metadata?.orderId

          await req.payload.update({
            collection: 'orders',
            id: orderId!,
            data: { status: 'paid', paymentId: session.payment_intent as string },
            req,
          })
        }

        return res.json({ received: true })
      },
    },
  ],
}

Глобальные эндпоинты в payload.config.ts

// payload.config.ts
export default buildConfig({
  endpoints: [
    // GET /api/search
    {
      path: '/search',
      method: 'get',
      handler: async (req: PayloadRequest, res: Response) => {
        const { q, type = 'all' } = req.query as { q: string; type: string }

        if (!q || q.length < 2) {
          return res.json({ docs: [], totalDocs: 0 })
        }

        const collections = type === 'all' ? ['posts', 'products', 'pages'] : [type]

        const results = await Promise.all(
          collections.map(collection =>
            req.payload.find({
              collection: collection as any,
              where: {
                or: [
                  { title: { like: q } },
                  { description: { like: q } },
                ],
              },
              limit: 5,
            })
          )
        )

        const docs = results.flatMap((r, i) =>
          r.docs.map(doc => ({ ...doc, _collection: collections[i] }))
        )

        return res.json({ docs, totalDocs: docs.length })
      },
    },

    // POST /api/contact
    {
      path: '/contact',
      method: 'post',
      handler: async (req: PayloadRequest, res: Response) => {
        const { name, email, message } = req.body

        if (!name || !email || !message) {
          return res.status(400).json({ error: 'All fields required' })
        }

        // Сохранить заявку
        await req.payload.create({
          collection: 'inquiries',
          data: { name, email, message, status: 'new' },
        })

        // Уведомить администраторов
        await emailService.send({
          to: process.env.ADMIN_EMAIL!,
          subject: `Новая заявка от ${name}`,
          text: `От: ${name} <${email}>\n\n${message}`,
        })

        return res.json({ success: true })
      },
    },
  ],
})

Middleware для API

// Логирование запросов к API
{
  path: '/admin-action',
  method: 'post',
  handler: async (req: PayloadRequest, res: Response) => {
    // Проверка аутентификации
    if (!req.user) {
      return res.status(401).json({ error: 'Unauthorized' })
    }

    // Проверка роли
    if (req.user.role !== 'admin') {
      return res.status(403).json({ error: 'Forbidden' })
    }

    // Логировать действие
    await req.payload.create({
      collection: 'audit-logs',
      data: {
        action: 'admin-action',
        user: req.user.id,
        timestamp: new Date().toISOString(),
        data: req.body,
      },
    })

    // Выполнить действие
    return res.json({ success: true })
  },
}

Вызов кастомных эндпоинтов

// Из Next.js Server Action
'use server'

export async function checkoutAction(items: CartItem[]) {
  const response = await fetch(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/orders/checkout`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ items, customerEmail: '[email protected]' }),
  })

  if (!response.ok) throw new Error('Checkout failed')
  return response.json()
}

Сроки

Разработка 3–5 кастомных эндпоинтов с интеграцией платёжной системы и вебхуками — 2–3 дня.