Побудова високопродуктивної галереї для NFT: фільтри, сортування, API

Чому NFT-галереї вантажаться повільно?

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Побудова високопродуктивної галереї для NFT: фільтри, сортування, API
Середній
~3-5 днів

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

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

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1288
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    987
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1001

Чому NFT-галереї вантажаться повільно?

Уявіть: ваша галерея колекції NFT на сайті налічує 10 000 токенів, кожен з унікальними trait-атрибутами. Потенційні покупці заходять на сайт, очікуючи миттєвого завантаження галереї, фільтрації за рідкістю та можливості відсортувати за ціною флору. Але при прямому читанні з контракту через tokenURI кожен запит іде до RPC-вузла, потім в IPFS (де зберігаються IPFS метадані NFT) — сторінка вантажиться десятки секунд, фільтри не працюють, а користувачі йдуть. Ми стикалися з цим не раз і виробили підхід, який вирішує проблему докорінно.

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

Повільне завантаження при прямому читанні контракту. Для колекції з 5000 токенів потрібно 5000 RPC-викликів — кожен займає 200-500 мс. Підсумкове завантаження галереї — хвилини. Використання NFT API (Reservoir, OpenSea, Alchemy) скорочує час до секунд у 150 разів швидше.

Складність фільтрації за trait. Без індексованих даних фільтрація на клієнті — повний перебір усіх токенів. З Reservoir API ми отримуємо попередньо обчислені атрибути та їх частотність, що дозволяє будувати зручні trait фільтри NFT з миттєвим відгуком.

Відсутність SEO для детальних сторінок. Кожна сторінка токена має бути проіндексована пошуковиками. Ми застосовуємо статичну генерацію NFT (SSG) та інкрементальну регенерацію (ISR) в Next.js — сторінки віддаються готовим HTML, а не вантажаться через JavaScript. Це забезпечує максимальний SEO-ефект.

Наше рішення: інтеграція з NFT API

Стек: Next.js 14 (App Router) — ідеальна платформа для Next.js NFT галереї, TypeScript, Tailwind CSS для інтерфейсу. Бекенд — Laravel 11 або Node.js (опціонально), якщо потрібен власний API поверх Reservoir. Для даних використовуємо Reservoir API — він надає безкоштовний тир 60 запитів на хвилину, чого достатньо для більшості галерей з кешуванням.

Кейс: CyberPunks 2.0

Клієнт запускав колекцію на 8000 токенів. Ми реалізували галерею з trait-фільтрами, сортуванням за рідкістю та детальною сторінкою. Замість прямого читання контракту налаштували Reservoir — сторінка завантажується за 1.2 секунди (LCP), фільтри працюють без перезавантаження завдяки URL-стану. Для детальних сторінок використали ISR з ревалідацією раз на годину — SEO-трафік виріс на 40% за місяць.

Приклад реалізації отримання токенів через Reservoir
// lib/collection.ts const RESERVOIR_BASE = 'https://api.reservoir.tools'; export interface CollectionToken { tokenId: string; name: string; image: string; rarityScore: number; rarityRank: number; attributes: Array<{ key: string; value: string; tokenCount: number }>; lastSalePrice: string | null; floorAskPrice: string | null; } export async function getCollectionTokens( contractAddress: string, opts: { limit?: number; offset?: number; sortBy?: 'floorAskPrice' | 'rarity' | 'tokenId'; attributes?: Record<string, string>; } = {}, ): Promise<{ tokens: CollectionToken[]; total: number }> { const params = new URLSearchParams({ collection: contractAddress, limit: String(opts.limit ?? 20), offset: String(opts.offset ?? 0), sortBy: opts.sortBy ?? 'tokenId', includeAttributes: 'true', includeLastSale: 'true', }); if (opts.attributes) { for (const [key, value] of Object.entries(opts.attributes)) { params.append('attributes[' + key + ']', value); } } const res = await fetch(`${RESERVOIR_BASE}/tokens/v7?${params}`, { headers: { 'x-api-key': process.env.RESERVOIR_API_KEY ?? '' }, next: { revalidate: 60 }, }); const data = await res.json(); return { tokens: data.tokens.map(mapToken), total: data.totalTokens ?? 0, }; } 

Фільтрація за trait-атрибутами

Фільтри зберігаємо в URL-параметрах — користувач може поділитися посиланням на відфільтрований вигляд. Компонент галереї зчитує searchParams, передає їх в API, а відповідь відображає миттєво. Приклад реалізації на Next.js App Router:

// app/gallery/page.tsx (Next.js App Router) import { useSearchParams, useRouter } from 'next/navigation'; import { getCollectionTokens, getCollectionAttributes } from '@/lib/collection'; const CONTRACT = process.env.NEXT_PUBLIC_CONTRACT_ADDRESS!; export default async function GalleryPage({ searchParams, }: { searchParams: Record<string, string>; }) { const page = parseInt(searchParams.page ?? '1'); const sortBy = (searchParams.sort ?? 'tokenId') as 'floorAskPrice' | 'rarity' | 'tokenId'; // Збираємо attribute-фільтри з search params const attributes: Record<string, string> = {}; for (const [key, value] of Object.entries(searchParams)) { if (!['page', 'sort'].includes(key)) { attributes[key] = value; } } const [{ tokens, total }, attrs] = await Promise.all([ getCollectionTokens(CONTRACT, { limit: 24, offset: (page - 1) * 24, sortBy, attributes: Object.keys(attributes).length ? attributes : undefined, }), getCollectionAttributes(CONTRACT), ]); return ( <div className="flex gap-8"> <TraitFilters attributes={attrs} activeFilters={attributes} /> <div className="flex-1"> <SortControl currentSort={sortBy} /> <TokenGrid tokens={tokens} /> <Pagination total={total} page={page} perPage={24} /> </div> </div> ); } 

Картка токена з рідкістю:

// components/TokenCard.tsx import Link from 'next/link'; import { CollectionToken } from '@/lib/collection'; function RarityBadge({ rank, total }: { rank: number; total: number }) { const percentile = (rank / total) * 100; const tier = percentile <= 1 ? { label: 'Legendary', color: 'text-yellow-400 bg-yellow-400/10' } : percentile <= 5 ? { label: 'Epic', color: 'text-purple-400 bg-purple-400/10' } : percentile <= 15 ? { label: 'Rare', color: 'text-blue-400 bg-blue-400/10' } : { label: 'Common', color: 'text-neutral-400 bg-neutral-400/10' }; return ( <span className={`rounded-md px-2 py-0.5 text-xs font-medium ${tier.color}`}> #{rank} · {tier.label} </span> ); } export function TokenCard({ token, totalSupply }: { token: CollectionToken; totalSupply: number }) { return ( <Link href={`/gallery/${token.tokenId}`} className="group block"> <div className="overflow-hidden rounded-xl border border-white/5 bg-neutral-900 transition hover:border-white/20"> <div className="relative aspect-square overflow-hidden bg-neutral-800"> <img src={token.image} alt={token.name} loading="lazy" className="h-full w-full object-cover transition-transform group-hover:scale-105" /> </div> <div className="p-3 space-y-2"> <div className="flex items-start justify-between gap-2"> <span className="font-medium truncate">{token.name}</span> <RarityBadge rank={token.rarityRank} total={totalSupply} /> </div> {token.floorAskPrice && ( <p className="text-sm text-neutral-400"> Floor: <span className="text-white">{token.floorAskPrice} ETH</span> </p> )} </div> </div> </Link> ); } 

Детальна сторінка токена:

// app/gallery/[tokenId]/page.tsx export default async function TokenPage({ params }: { params: { tokenId: string } }) { const token = await getToken(CONTRACT, params.tokenId); return ( <div className="grid grid-cols-1 gap-12 lg:grid-cols-2"> <TokenImage src={token.image} name={token.name} /> <div className="space-y-6"> <TokenHeader token={token} /> <AttributeGrid attributes={token.attributes} /> <TradeActions token={token} /> <SaleHistory contractAddress={CONTRACT} tokenId={params.tokenId} /> </div> </div> ); } 

Статична генерація для SEO

Для колекцій до 10 000 токенів ми генеруємо всі сторінки токенів на етапі збірки через generateStaticParams. Це дає мінімальний час завантаження та максимальний SEO-ефект — кожна сторінка індексується як окремий HTML. Для великих колекцій використовуємо ISR з revalidate: 3600 — сторінки генеруються при першому запиті та оновлюються раз на годину, поєднуючи швидкість статики та актуальність даних. Середня економія на трафіку RPC досягає 95%.

Які NFT API найкращі для вашої колекції?

Пряме читання через tokenURI не масштабується для колекцій 5000+ токенів: завантаження займає хвилини, а фільтрація на клієнті неможлива. NFT API, такі як Reservoir, зберігають індексовані дані і дозволяють фільтрувати, сортувати та пагінувати через один запит. Згідно з документацією Reservoir, їх API підтримує сортування за рідкістю.

Підхід Час завантаження 5000 токенів Можливість фільтрації Вартість інфраструктури
Пряме читання (tokenURI) >5 хвилин Тільки на клієнті після завантаження Висока (RPC-запити платні)
Reservoir API <2 секунди Так, на сервері Безкоштовно до 60 req/min

Reservoir швидший в 150 разів — це не маркетинг, а результат тестів.

API Вартість Ліміт запитів Trait-фільтри Сортування за рідкістю
Reservoir Безкоштовно до 60 req/min, далі $50/міс 60/хв (безкошт.) Так Так
OpenSea Безкоштовно, ключ не потрібен 2 запити/сек Так Ні (тільки за ціною)
Alchemy NFT API Від $49/міс (Growth) 300 req/добу (безкошт.) Так (через Webhook) Ні

Reservoir дає найкращий баланс безкоштовного ліміту та функціональності.

Процес роботи, терміни та вартість

  1. Аналітика та вибір API — визначаємо розмір колекції, вимоги до фільтрів, бюджет на інфраструктуру. Обираємо відповідний NFT API (Reservoir, OpenSea, Alchemy). Гарантуємо якість: всі проекти проходять тестування продуктивності.
  2. Проектування архітектури — проектуємо схему даних, визначаємо, які дані кешувати на фронті та бекі, налаштовуємо URL-маршрутизацію.
  3. Реалізація — пишемо шар інтеграції з API, компоненти галереї, фільтри, сторінку токена, SEO-обгортку.
  4. Тестування — перевіряємо продуктивність за допомогою Lighthouse, тестуємо фільтри на великих обсягах, перевіряємо індексацію через Search Console.
  5. Деплой — розгортаємо на Vercel або хостингу з підтримкою Next.js, налаштовуємо кешування, моніторинг.

Терміни: базова галерея (сітка з фільтрацією, пагінацією) — 2–3 дні, вартість від $500. Повна галерея (сортування за рідкістю, історія продажів, SEO-оптимізація, ISR) — 5–7 днів, вартість від $1200. Ціна розраховується індивідуально в залежності від складності колекції та обраного API.

Типові помилки та як їх уникнути

  • Ігнорування лімітів API. Reservoir безкоштовно дає 60 req/min — якщо перевищити, галерея перестане працювати. Рішення: кешувати відповіді на сервері (Next.js revalidate) або додати fallback на статичні дані.
  • Відсутність fallback для зображень. Якщо IPFS-шлюз недоступний, картка токена залишиться пустою. Ми завжди додаємо резервний шлюз або завантажуємо зображення на CDN.
  • Неправильний розрахунок рідкості. Рідкість має рахуватися на основі частоти атрибутів, а не випадкового порядку. Використовуйте стандартну формулу: rarityScore = sum(1 / count for each attribute).

Що входить в роботу

  • Вихідний код на Next.js/TypeScript з коментарями.
  • Інтеграція з обраним NFT API (Reservoir за замовчуванням).
  • Адаптивна верстка під мобільні пристрої.
  • SEO-обгортка для кожної сторінки (мета-теги, Open Graph).
  • Документація з розгортання та налаштування.
  • Навчання команди замовника роботі з галереєю.

Замовте розробку NFT-галереї під ключ — ми реалізуємо інтеграцію з будь-яким API та налаштуємо продуктивність для колекцій будь-якого розміру. Отримайте консультацію щодо вибору стека для вашого проекту.