Розробка бекенду dApp на Node.js

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

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

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

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

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

Розробка DApp бекенду на Node.js

Не кожному dApp потрібен бекенд — це перше, що потрібно зрозуміти. Якщо логіка повністю on-chain, а фронтенд читає дані через публічний RPC — бекенд є зайвим. Але щойно з'являються: приватні API ключі, агрегація даних з кількох джерел, кешування дорогих on-chain запитів, безгазові транзакції (relayer), верифікація підписів — без бекенду не обійтись.

Ключові компоненти

RPC абстракційний шар

Прямий доступ із фронтенду до Infura/Alchemy розкриває API ключ. Бекенд проксирує RPC виклики, додає кешування та rate limiting:

import { JsonRpcProvider, Contract } from 'ethers';
import Fastify from 'fastify';

const provider = new JsonRpcProvider(process.env.RPC_URL);

const app = Fastify();

// Кешований endpoint для даних контракту
app.get('/contract/:address/balance/:account', {
  config: { rateLimit: { max: 100, timeWindow: '1 minute' } }
}, async (req, reply) => {
  const { address, account } = req.params as { address: string; account: string };
  const cacheKey = `balance:${address}:${account}`;
  
  const cached = await redis.get(cacheKey);
  if (cached) return { balance: cached, cached: true };
  
  const contract = new Contract(address, ERC20_ABI, provider);
  const balance = await contract.balanceOf(account);
  
  await redis.setex(cacheKey, 12, balance.toString()); // кеш на ~1 блок (12 сек)
  return { balance: balance.toString(), cached: false };
});

Верифікація підписів (аутентифікація)

Sign-In With Ethereum (EIP-4361) — стандарт аутентифікації без пароля. Користувач підписує SIWE-повідомлення, бекенд верифікує підпис та видає JWT:

import { SiweMessage } from 'siwe';
import jwt from 'jsonwebtoken';

app.post('/auth/verify', async (req, reply) => {
  const { message, signature } = req.body;
  
  const siweMessage = new SiweMessage(message);
  const result = await siweMessage.verify({ signature });
  
  if (!result.success) {
    return reply.code(401).send({ error: 'Invalid signature' });
  }
  
  const token = jwt.sign(
    { address: result.data.address, chainId: result.data.chainId },
    process.env.JWT_SECRET!,
    { expiresIn: '7d' }
  );
  
  return { token };
});

Nonce для захисту від replay атак: генеруємо випадковий nonce, зберігаємо в Redis з TTL 5 хвилин, верифікуємо що nonce в SIWE повідомленні збігається з виданим. Після використання — видаляємо.

Індексація подій

On-chain події необхідні для відображення історії операцій, сповіщень, аналітики. Два підходи:

Polling (простий): кожні N секунд опитуємо getLogs за останні блоки. Працює, але з затримкою та витрачає CU провайдера.

WebSocket підписка (правильна):

const wsProvider = new WebSocketProvider(process.env.WSS_RPC_URL);

const contract = new Contract(CONTRACT_ADDRESS, ABI, wsProvider);

contract.on('Transfer', async (from, to, value, event) => {
  await db.transfers.insert({
    from,
    to,
    value: value.toString(),
    blockNumber: event.log.blockNumber,
    txHash: event.log.transactionHash,
    timestamp: new Date(),
  });
  
  // Сповістити підписників через WebSocket/SSE
  eventBus.emit('transfer', { from, to, value: value.toString() });
});

// Обробка розриву з'єднання
wsProvider.on('error', async () => {
  console.error('WS disconnected, reconnecting...');
  setTimeout(setupSubscriptions, 5000);
});

WebSocket з'єднання нестійкі — reconnect логіка обов'язкова. Альтернатива для production: Alchemy webhooks, Quicknode Streams — провайдер сам доставляє події на ваш HTTP endpoint.

Безгазові транзакції (meta-транзакції)

EIP-2771 + ERC-2612 дозволяють користувачу підписати транзакцію офф-чейн, а relayer оплачує газ. Бекенд працює як relayer:

app.post('/relay/transfer', authenticateJWT, async (req, reply) => {
  const { permit, signature } = req.body; // ERC-2612 permit
  
  // Верифікуємо permit підпис
  const tokenContract = new Contract(TOKEN_ADDRESS, ERC20_ABI, wallet);
  
  // Перевіряємо що permit валіден та не істьок
  const nonce = await tokenContract.nonces(permit.owner);
  if (BigInt(permit.nonce) !== nonce) {
    return reply.code(400).send({ error: 'Invalid nonce' });
  }
  
  // Виконуємо permit + transferFrom за користувача
  const tx = await tokenContract.permit(
    permit.owner, permit.spender, permit.value,
    permit.deadline, permit.v, permit.r, permit.s
  );
  await tx.wait();
  
  return { txHash: tx.hash };
});

Для production безгазових транзакцій: OpenZeppelin Defender Relayer або Biconomy — вони керують nonce, retry логікою та моніторингом застрявших транзакцій.

Структура проекту

src/
  api/          # HTTP маршрути (Fastify/Express)
  blockchain/   # Provider, контракти, слухачі подій
  services/     # Бізнес-логіка
  workers/      # BullMQ воркери для фонових завдань
  db/           # Prisma схема, міграції
  cache/        # Redis клієнт
  middleware/   # Auth, rate limiting, валідація

Fastify швидше Express на ~15-20% за пропускною здатністю та має вбудовану JSON schema валідацію. Для dApp бекенду різниця рідко критична, але fastify-plugin екосистема зручна.

Моніторинг та надійність

Застрявлі транзакції: транзакція з низькою gasPrice зависає в mempool. Моніторимо через polling getTransactionReceipt(). Після N хвилин — піднімаємо газ (resend з тим же nonce, gasPrice * 1.1).

Управління nonce: при паралельних транзакціях з одного гаманця потрібен атомарний лічильник nonce. Redis INCR + pending nonce tracking, або бібліотека типу ethers-multicall.

Circuit breaker для RPC: якщо провайдер повертає помилки — перемикаємось на резервний. Паттерн circuit breaker через opossum або вручну.

Ориентиры по срокам

Базовий бекенд (RPC proxy + SIWE auth + кешування) — 2-3 дні. Event indexer + WebSocket push + безгазовий relay — ще 3-4 дні. Production-готовий з моніторингом, retry логікою та fallback RPC — 1,5-2 тижні.