Быстрая NFT-галерея: trait-фильтры, сортировка по редкости, API

Проблема медленной загрузки NFT-галерей

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

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

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Быстрая NFT-галерея: trait-фильтры, сортировка по редкости, 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
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    1001

Проблема медленной загрузки NFT-галерей

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

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

Медленная загрузка при прямом чтении контракта. Для коллекции из 5000 токенов потребуется 5000 RPC-вызовов — каждый занимает 200-500 мс. Итоговая загрузка галереи — минуты. Использование NFT API (Reservoir, OpenSea, Alchemy) сокращает время до секунд.

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

Отсутствие SEO для детальных страниц. Каждая страница токена должна быть проиндексирована поисковиками. Мы применяем статическую генерацию (SSG) и инкрементальную регенерацию (ISR) в Next.js — страницы отдаются готовым HTML, а не грузятся через JavaScript.

Наше решение: интеграция с NFT API

Стек: Next.js 14 (App Router) на фронте, 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 дня. Полная галерея (сортировка по редкости, история продаж, SEO-оптимизация, ISR) — 5–7 дней. Стоимость рассчитывается индивидуально в зависимости от сложности коллекции и выбранного 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 и настроим производительность для коллекций любого размера. Получите консультацию по выбору стека для вашего проекта.