Кастомные сервисы в Strapi
Сервис в Strapi — слой бизнес-логики, вызываемый из контроллеров или других сервисов. Стандартные сервисы (find, findOne, create, update, delete) генерируются автоматически. Кастомный сервис добавляет методы, которые инкапсулируют сложную логику и переиспользуются в нескольких местах.
Базовый кастомный сервис
// src/api/order/services/order.ts
import { factories } from '@strapi/strapi'
export default factories.createCoreService('api::order.order', ({ strapi }) => ({
// Переопределить create — добавить бизнес-логику
async create(params) {
const order = await super.create(params)
// Отправить подтверждение
await strapi.plugin('email').service('email').send({
to: order.customerEmail,
from: '[email protected]',
subject: `Заказ #${order.orderNumber} принят`,
html: `<p>Ваш заказ принят. Номер: ${order.orderNumber}</p>`,
})
// Создать запись в CRM
await strapi.service('api::crm.crm').createDeal(order)
return order
},
// Кастомный метод
async processPayment(orderId: number, paymentData: any) {
const order = await strapi.entityService.findOne('api::order.order', orderId, {
populate: ['items', 'items.product'],
})
if (!order) throw new Error('Order not found')
if (order.status !== 'pending') throw new Error('Order is not pending')
// Обработать платёж через платёжный шлюз
const paymentResult = await this.chargeCard(order.total, paymentData)
if (paymentResult.success) {
await strapi.entityService.update('api::order.order', orderId, {
data: {
status: 'paid',
paymentId: paymentResult.transactionId,
paidAt: new Date().toISOString(),
},
})
// Уменьшить остатки
await this.decrementStock(order.items)
return { success: true, orderId }
} else {
await strapi.entityService.update('api::order.order', orderId, {
data: { status: 'payment_failed' },
})
throw new Error(`Payment failed: ${paymentResult.error}`)
}
},
async decrementStock(items: any[]) {
await Promise.all(
items.map(async (item) => {
const product = await strapi.entityService.findOne(
'api::product.product',
item.product.id
)
const newStock = Math.max(0, product.stock - item.quantity)
await strapi.entityService.update('api::product.product', item.product.id, {
data: { stock: newStock },
})
})
)
},
async chargeCard(amount: number, paymentData: any) {
// Интеграция с платёжным шлюзом
const response = await fetch('https://api.payment-gateway.com/charge', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PAYMENT_SECRET}` },
body: JSON.stringify({ amount, ...paymentData }),
})
return response.json()
},
// Получить аналитику заказов
async getOrderStats(startDate: Date, endDate: Date) {
const orders = await strapi.entityService.findMany('api::order.order', {
filters: {
createdAt: { $gte: startDate.toISOString(), $lte: endDate.toISOString() },
status: { $in: ['paid', 'shipped', 'delivered'] },
},
})
const total = orders.reduce((sum: number, o: any) => sum + (o.total || 0), 0)
const count = orders.length
const avgOrder = count > 0 ? total / count : 0
return { total, count, avgOrder, orders }
},
}))
Standalone сервис (не связан с content type)
// src/api/email-notifications/services/email-notifications.ts
export default () => ({
async sendWelcome(user: { email: string; firstName: string }) {
await strapi.plugin('email').service('email').send({
to: user.email,
subject: `Добро пожаловать, ${user.firstName}!`,
html: await strapi.service('api::email-templates.email-templates')
.render('welcome', { user }),
})
},
async sendPasswordReset(email: string, token: string) {
const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${token}`
await strapi.plugin('email').service('email').send({
to: email,
subject: 'Сброс пароля',
html: `<a href="${resetUrl}">Сбросить пароль</a>`,
})
},
})
Вызов сервиса из контроллера
// src/api/order/controllers/order.ts
async checkout(ctx) {
const { items, paymentData } = ctx.request.body
// Создать заказ
const order = await strapi.service('api::order.order').create({
data: {
items,
customer: ctx.state.user.id,
status: 'pending',
},
})
// Обработать платёж
const result = await strapi.service('api::order.order').processPayment(
order.id,
paymentData
)
return result
}
Сервис с кэшированием
// src/api/catalog/services/catalog.ts
const cache = new Map<string, { data: any; ts: number }>()
const TTL = 60_000 // 1 минута
export default () => ({
async getCategories() {
const cacheKey = 'categories'
const cached = cache.get(cacheKey)
if (cached && Date.now() - cached.ts < TTL) {
return cached.data
}
const data = await strapi.entityService.findMany('api::category.category', {
filters: { active: { $eq: true } },
populate: ['icon', 'children'],
sort: { order: 'asc' },
})
cache.set(cacheKey, { data, ts: Date.now() })
return data
},
})
Сроки
Разработка бизнес-сервисов для e-commerce (заказы, оплата, инвентарь) — 3–5 дней.







