Розробка API для доступу до зібраних даних

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка API для доступу до зібраних даних
Середній
~3-5 днів
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1286
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Розробка API для доступу до зібраних даних

Зібрати дані — це половина завдання. Друга половина — зробити їх доступними у форматі, який не гальмує клієнтів, не падає під навантаженням й не розповідає всім підряд що ви парсили останні пів року. API для blockchain/crypto даних має специфіку: дані immutable й append-only (історичні записи не змінюються), обсяги великі (мільйони транзакцій), запити часто time-series за природою, клієнти хочуть і REST для простих запитів й WebSocket для real-time.

Дизайн REST API

Версіонування й структура URL

/v1/funding-rates/{symbol}?exchange=binance&from=2024-01-01&to=2024-03-01&limit=500
/v1/transactions/{chain}/{address}?from_block=19000000&limit=100
/v1/news?tags=bitcoin,regulation&from=2024-01-15T00:00:00Z&limit=50
/v1/gas/history?network=ethereum&granularity=1h&from=2024-01-01

Принципи:

  • limit + cursor пагінація замість page + offset — стабільна при вставці нових даних
  • Часові параметри в ISO 8601 (включаючи timezone) або unix milliseconds — приймаємо обидва
  • ?fields= для field selection — не повертаємо 30 полів якщо клієнт використовує 3

Cursor-based пагінація

interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    cursor: string | null;  // null = остання сторінка
    hasMore: boolean;
    total?: number;         // опціонально, дорого рахується
  };
}

// Cursor = base64(JSON({lastId, lastTimestamp}))
// Запит наступної сторінки: GET /v1/funding-rates?cursor={opaque_string}

Додавання нових записів на початок не ломає навігацію клієнта по історії.

Query параметри й валідація

Zod для валідації на вході — описує схему й конвертує типи:

import { z } from "zod";

const FundingRatesQuerySchema = z.object({
  symbol:   z.string().regex(/^[A-Z]+-[A-Z]+$/, "Invalid symbol format"),
  exchange: z.enum(["binance", "bybit", "okx", "hyperliquid"]).optional(),
  from:     z.coerce.date(),
  to:       z.coerce.date(),
  limit:    z.coerce.number().min(1).max(1000).default(100),
  cursor:   z.string().optional(),
});

type FundingRatesQuery = z.infer<typeof FundingRatesQuerySchema>;

Ранній return 400 з детальними помилками економить час клієнтів:

{
  "error": "VALIDATION_ERROR",
  "details": [
    { "field": "from", "message": "Invalid date format" },
    { "field": "limit", "message": "Must be between 1 and 1000" }
  ]
}

Производительність: кешування й оптимізація запитів

Багаторівневий кеш

Client → CDN (статичні історичні дані, TTL 1h)
       → Application cache (Redis, TTL 30s–5m)
       → Read replica PostgreSQL / ClickHouse

Redis кешування по ключу запиту:

async function getFundingRates(query: FundingRatesQuery): Promise<FundingRateRecord[]> {
  const cacheKey = `fr:${query.symbol}:${query.exchange ?? "all"}:${query.from.getTime()}:${query.to.getTime()}`;
  
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);
  
  const data = await db.queryFundingRates(query);
  
  // Історичні дані (минуле) кешуємо довго, real-time - коротко
  const ttl = query.to < new Date(Date.now() - 3600_000) ? 3600 : 30;
  await redis.setEx(cacheKey, ttl, JSON.stringify(data));
  
  return data;
}

Database query optimization

Для time-series даних у PostgreSQL — індекси критичні:

-- Покривний індекс для типового запиту
CREATE INDEX CONCURRENTLY idx_funding_rates_lookup 
ON funding_rates (symbol, exchange, settled_at DESC)
INCLUDE (funding_rate, mark_price);

-- Запит повинен використовувати цей індекс:
EXPLAIN ANALYZE
SELECT settled_at, funding_rate, mark_price
FROM funding_rates
WHERE symbol = 'BTC-USDT'
  AND exchange = 'binance'
  AND settled_at BETWEEN $1 AND $2
ORDER BY settled_at DESC
LIMIT 100;

Для аналітичних запитів (агрегації, середні по періодам) — ClickHouse значно швидше PostgreSQL.

WebSocket для real-time даних

// Fastify + @fastify/websocket
fastify.get("/v1/stream", { websocket: true }, (socket, req) => {
  const subscriptions = parseSubscriptions(req.query);
  
  const unsubscribers = subscriptions.map((sub) =>
    eventBus.on(sub.channel, (data) => {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ channel: sub.channel, data }));
      }
    })
  );
  
  socket.on("message", (msg) => {
    const cmd = JSON.parse(msg.toString());
    if (cmd.type === "subscribe") { /* ... */ }
    if (cmd.type === "unsubscribe") { /* ... */ }
    if (cmd.type === "ping") socket.send(JSON.stringify({ type: "pong" }));
  });
  
  socket.on("close", () => unsubscribers.forEach(unsub => unsub()));
});

Heartbeat: сервер шле ping кожні 30 секунд, клієнт повинен ответити pong. Без ответу за 10 сек — закриваємо з'єднання. Це відсікає зависшыні з'єднання які займають memory.

Rate limiting й auth

API keys замість JWT для server-to-server: не дійснюють, легко інвалідувати, не потребують refresh flow:

// Middleware
async function apiKeyAuth(req: FastifyRequest, reply: FastifyReply) {
  const key = req.headers["x-api-key"];
  if (!key) return reply.code(401).send({ error: "API key required" });
  
  const apiKey = await redis.hGetAll(`apikey:${key}`);
  if (!apiKey.id) return reply.code(401).send({ error: "Invalid API key" });
  
  req.apiKeyId = apiKey.id;
  req.rateLimitTier = apiKey.tier; // "free", "standard", "premium"
}

Sliding window rate limiting через Redis Lua script — атомарна операція:

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

redis.call("ZREMRANGEBYSCORE", key, 0, now - window)
local count = redis.call("ZCARD", key)
if count >= limit then return 0 end
redis.call("ZADD", key, now, now)
redis.call("EXPIRE", key, window / 1000)
return 1

Rate limit headers у ответах: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset — стандартна практика, клієнти можуть адаптувати своє поведінку.

Observability

Structured logging кожного запиту з: api_key_id, endpoint, query_params (без секретів), response_time_ms, status_code, cache_hit.

Prometheus метрики:

const httpRequestDuration = new Histogram({
  name: "http_request_duration_ms",
  help: "HTTP request duration in milliseconds",
  labelNames: ["method", "route", "status_code"],
  buckets: [5, 10, 25, 50, 100, 250, 500, 1000, 2500],
});

P99 latency по кожному endpoint — головний SLA метрик. Alert якщо P99 > 500ms для кешованих запитів або > 2000ms для некешованих.

Стек: Fastify (Node.js) для REST + WebSocket, Redis для кеша й rate limiting, PostgreSQL/ClickHouse для даних, Prometheus + Grafana для моніторингу.

Реалістичний срок розробки API поверх готової БД з даними: 4–7 тижнів включаючи auth, rate limiting, документацію (OpenAPI spec).