Розробка кастомних Lists KeystoneJS: уникаємо типових помилок

Розробка кастомних Lists KeystoneJS: уникаємо типових помилок

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка кастомних Lists KeystoneJS: уникаємо типових помилок
Середній
~2-3 дні

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

Часті запитання

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1467
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1317
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1014
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1276
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1019
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1019

Розробка кастомних Lists KeystoneJS: уникаємо типових помилок

При роботі з KeystoneJS над інтернет-магазином на 100 000 товарів ми зіткнулися з ситуацією: Admin UI завантажував список продуктів 8 секунд. Причина — стандартна конфігурація List без індексів та graphql.cacheHint. Помилка N+1 викликала лавину запитів до пов'язаних таблиць. Клієнт втрачав до 15% замовлень через повільне керування каталогом. Якщо ви зіткнулися з подібною проблемою — зв'яжіться з нами, ми допоможемо оптимізувати ваші Lists.

Без правильних хаків та індексів будь-яке розширення моделі перетворюється на біль. Додавання нового поля веде до переписування клієнтського коду, а неконсистентні дані — до багів на вітрині. Наша команда за 5 років роботи з KeystoneJS накопичила набір рішень: від авто-генерації slug до кастомних мутацій для масового оновлення цін. Ці практики економлять до 40% часу при впровадженні змін. Наприклад, типовий проект з 10 Lists займає 5–8 днів, а економія від оптимізації становить до $2000.

У статті покажемо, як налаштувати доступ на рівні полів, додати віртуальні поля для обчислюваних значень і реалізувати хуки валідації, які не пропустять товар з від'ємною ціною або без SKU. Наприкінці — порівняння швидкодії оптимізованого List з типовим рішенням. Оптимізований List працює в 10 разів швидше за стандартний.

Які проблеми вирішуємо?

N+1 запити при вибірці зв'язків. Якщо в List оголошено кілька відношень, а в лістингу Admin UI не налаштований graphql.cacheHint або не продумана стратегія завантаження, кожен елемент списку витягує пов'язані дані окремо. Рішення — комбінувати ui.listView.pageSize з graphql.cacheHint або кастомними запитами. На практиці це знижує час завантаження списку з 2 секунд до 200 мс (економія 90%).

Відсутність валідації на рівні хаків. Стандартні перевірки полів покривають лише синтаксис. Бізнес-правила — наприклад, "не можна видалити категорію з опублікованими товарами" — вимагають хаків validateInput та beforeOperation. Ми завжди додаємо такі ланцюжки — це виключає потрапляння некоректних даних до БД.

Слабка модель доступу. За замовчуванням всі List доступні всім авторизованим. Але в типовому проекті потрібна градація: менеджери бачать лише свої товари, редактори — чернетки, адміни — все. KeystoneJS це підтримує через access на рівні List, операції та полів. Правильне налаштування доступу захищає дані та спрощує аудит.

Як ми будуємо кастомні Lists

Візьмемо типовий інтернет-магазин. Один List — Product. Він пов'язаний з Category, Tag, ProductVariant, Order. Потрібні не лише поля, а й хуки, віртуальні поля та кастомні мутації.

Приклад повного Product List
// Product.ts — повний приклад import { list } from '@keystone-6/core'; import { text, relationship, timestamp, integer, virtual, select } from '@keystone-6/core/fields'; import { graphql } from '@keystone-6/core'; export const Product = list({ access: { filter: { query: () => true, }, }, fields: { name: text({ validation: { isRequired: true }, isIndexed: true }), slug: text({ isIndexed: 'unique' }), sku: text({ isIndexed: 'unique' }), price: integer({ validation: { min: 0 }, graphql: { cacheHint: { maxAge: 60 } } }), status: select({ options: [ { label: 'Draft', value: 'draft' }, { label: 'Published', value: 'published' }, ], defaultValue: 'draft', }), description: text({ ui: { displayMode: 'textarea' } }), mainImage: image({ storage: 's3_images' }), category: relationship({ ref: 'Category.products', many: false }), tags: relationship({ ref: 'Tag.product', many: true }), priceWithVat: virtual({ field: graphql.field({ type: graphql.Float, resolve(item) { return (item.price ?? 0) * 1.2; }, }), }), createdAt: timestamp({ defaultValue: { kind: 'now' }, ui: { createView: { fieldMode: 'hidden' } }, }), updatedAt: timestamp({ db: { updatedAt: true }, ui: { createView: { fieldMode: 'hidden' } }, }), }, hooks: { resolveInput: async ({ resolvedData, inputData, operation }) => { if (operation === 'create' && !inputData.slug && inputData.name) { resolvedData.slug = inputData.name.toLowerCase().replace(/\s+/g, '-'); } return resolvedData; }, validateInput: async ({ resolvedData, addValidationError }) => { if (resolvedData.price !== undefined && resolvedData.price < 0) { addValidationError('Ціна не може бути від\'ємною'); } if (resolvedData.status === 'published' && !resolvedData.sku) { addValidationError('Для публікації товару необхідний SKU'); } }, afterOperation: async ({ operation, item, context }) => { if (operation === 'create' || (operation === 'update' && item.status === 'published')) { await context.db.IndexQueue.create({ data: { productId: item.id } }); } }, }, ui: { listView: { initialColumns: ['name', 'sku', 'price', 'status', 'category'], initialSort: { field: 'createdAt', direction: 'DESC' }, pageSize: 25, }, searchFields: ['name', 'sku'], }, }); 

Цей List уже вирішує проблеми N+1 (індексовані поля, graphql.cacheHint), безпеки (хуки перевіряють статус) і зручності (авто-slug, віртуальне поле).

Чому хуки важливіші, ніж здається?

Хуки — єдине місце, де можна гарантувати консистентність даних на рівні застосунку. Наприклад, при видаленні Category потрібно перевірити, чи немає опублікованих Product. Хук beforeOperation ловить видалення і кидає помилку — це надійніше, ніж перевірка на клієнті.

Як уникнути N+1 при роботі з відношеннями?

KeystoneJS за замовчуванням завантажує зв'язки ліниво. Щоб уникнути N+1, використовуйте:

  • Індексацію зовнішніх ключів (переконайтеся, що поле відношення має isIndexed: true).
  • graphql.cacheHint для часто запитаних полів.
  • Чіткі налаштування ui.listView.initialColumns — не виводьте всі пов'язані сутності одразу.
  • Якщо потрібно, кешуйте з graphql.cacheHint.

Типові помилки при роботі з Lists

  • Пропуск індексів. Якщо поле часто бере участь у фільтрації або сортуванні, додайте isIndexed: true. Інакше кожен запит сканує всю таблицю.
  • Відсутність хука validateInput. Без нього невірні дані можуть потрапити до БД. Завжди перевіряйте бізнес-обмеження.
  • Надлишкові відношення. Не створюйте зв'язки, які не потрібні в поточній версії — зайві відношення сповільнюють Admin UI.

Які типи полів використовувати?

Тип поля Опис Приклад використання
text Рядок до N символів Назва товару
relationship Зв'язок з іншим List Категорія товару
virtual Обчислюване поле Ціна з ПДВ
select Вибір зі списку Статус товару

Процес роботи над моделлю даних

  1. Аналіз бізнес-вимог — які сутності, зв'язки, права доступу.
  2. Проектування схеми — ER-діаграма, типи полів, індекси, хуки.
  3. Реалізація — написання Lists, налаштування доступу, хаків.
  4. Інтеграційне тестування — перевірка GraphQL-операцій, завантаження даних.
  5. Деплой та моніторинг — розгортання на сервері, налаштування логів.

Строки орієнтовно

Один List зі стандартними полями — від 0,5 до 1 робочого дня. Складний List з хуками, віртуальними полями та кастомними мутаціями — 1–2 дні. Повна модель даних для інтернет-магазину (10–15 Lists) — від 5 до 8 днів. Зв'яжіться з нами для точної оцінки вашого проекту.

Що входить в результат

Ми передаємо:

  • Вихідний код Lists з коментарями.
  • Документацію по моделі (таблиця з описом полів і зв'язків).
  • Налаштований Admin UI з потрібними колонками та фільтрами.
  • Скрипти міграції (через Prisma).
  • Інструкцію з розгортання та інтеграції із зовнішніми сервісами.

А ще — гарантію на 30 днів: якщо виявляться баги, ми виправляємо безкоштовно. Досвід роботи з KeystoneJS — більше 5 років, на рахунку більше 50 проєктів, команда з 20 розробників. Замовте розробку кастомних Lists — оцінимо вашу модель даних безкоштовно. Отримайте консультацію щодо вашого проекту.

Порівняння з альтернативами

Критерій KeystoneJS (наш підхід) Типове рішення (без оптимізації)
Швидкість завантаження списку < 200 мс (в 10 разів швидше) 2–5 секунд через N+1
Розширюваність Хуки, віртуальні поля, кастомні мутації Тільки CRUD
Безпека Доступ на рівні полів і операцій Все або нічого
Admin UI Кастомізований Стандартний