Разработка JSON-RPC API для веб-приложения
JSON-RPC — простой протокол удалённого вызова процедур с использованием JSON. Версия 2.0 — текущий стандарт. В отличие от REST, нет концепции ресурсов — только методы и параметры. Популярен в блокчейн-экосистеме (Ethereum JSON-RPC, Bitcoin RPC), языковых серверах (LSP), некоторых платёжных API.
Спецификация JSON-RPC 2.0
Запрос:
{
"jsonrpc": "2.0",
"method": "user.getById",
"params": { "id": 42 },
"id": 1
}
Успешный ответ:
{
"jsonrpc": "2.0",
"result": { "id": 42, "name": "Иван Петров", "email": "[email protected]" },
"id": 1
}
Ошибка:
{
"jsonrpc": "2.0",
"error": { "code": -32602, "message": "Invalid params", "data": { "field": "id" } },
"id": 1
}
Notification (без id — ответ не нужен):
{ "jsonrpc": "2.0", "method": "log.event", "params": { "type": "page_view" } }
Batch (несколько запросов в одном HTTP-запросе):
[
{ "jsonrpc": "2.0", "method": "user.get", "params": { "id": 1 }, "id": 1 },
{ "jsonrpc": "2.0", "method": "user.get", "params": { "id": 2 }, "id": 2 }
]
Коды ошибок
Стандартные коды из спецификации:
| Код | Значение |
|---|---|
| -32700 | Parse error — невалидный JSON |
| -32600 | Invalid Request — неверный объект запроса |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
| -32000 до -32099 | Серверные ошибки (определяются реализацией) |
Реализация сервера (Node.js)
import express from 'express';
const methods: Record<string, (params: any, ctx: Context) => Promise<any>> = {
'user.getById': async ({ id }, ctx) => {
const user = await ctx.db.user.findUnique({ where: { id } });
if (!user) throw { code: -32000, message: 'User not found' };
return user;
},
'user.create': async ({ name, email }, ctx) => {
if (!ctx.user) throw { code: -32001, message: 'Unauthorized' };
return ctx.db.user.create({ data: { name, email } });
},
};
app.post('/rpc', async (req, res) => {
const requests = Array.isArray(req.body) ? req.body : [req.body];
const responses = await Promise.all(requests.map(async (request) => {
const { jsonrpc, method, params, id } = request;
if (jsonrpc !== '2.0') {
return id != null
? { jsonrpc: '2.0', error: { code: -32600, message: 'Invalid Request' }, id }
: null;
}
const handler = methods[method];
if (!handler) {
return id != null
? { jsonrpc: '2.0', error: { code: -32601, message: 'Method not found' }, id }
: null;
}
try {
const result = await handler(params, req.ctx);
return id != null ? { jsonrpc: '2.0', result, id } : null;
} catch (error: any) {
return id != null
? { jsonrpc: '2.0', error: { code: error.code ?? -32603, message: error.message }, id }
: null;
}
}));
const filteredResponses = responses.filter(Boolean);
res.json(Array.isArray(req.body) ? filteredResponses : filteredResponses[0]);
});
JSON-RPC over WebSocket
JSON-RPC может работать не только через HTTP POST, но и через WebSocket — для двунаправленных RPC:
// Клиент ожидает ответ по id
const pendingRequests = new Map<number, { resolve, reject }>();
let requestId = 0;
function callMethod(method: string, params: any): Promise<any> {
return new Promise((resolve, reject) => {
const id = ++requestId;
pendingRequests.set(id, { resolve, reject });
ws.send(JSON.stringify({ jsonrpc: '2.0', method, params, id }));
});
}
ws.onmessage = ({ data }) => {
const { id, result, error } = JSON.parse(data);
const pending = pendingRequests.get(id);
if (!pending) return;
error ? pending.reject(error) : pending.resolve(result);
pendingRequests.delete(id);
};
Когда использовать JSON-RPC
- Интеграция с блокчейн-нодами (Ethereum
eth_call,eth_getBalance) - Language Server Protocol (LSP) для IDE-плагинов
- Простые RPC-сервисы, где REST-ресурсная модель неудобна
- Когда нужен batch-запрос нескольких операций в одном HTTP-вызове
Сроки
JSON-RPC сервер (10–20 методов, валидация, batch, аутентификация): 1–1.5 недели.







