{"id":21354449,"url":"https://github.com/ya-kostik/small-rpc","last_synced_at":"2025-07-12T22:31:57.097Z","repository":{"id":32653017,"uuid":"138902316","full_name":"ya-kostik/small-rpc","owner":"ya-kostik","description":"Простой RPC для проекта. Можно использовать как с HTTP, так и с сокетами, так и с любым другим транспортом.","archived":false,"fork":false,"pushed_at":"2023-01-07T04:15:13.000Z","size":427,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-30T21:43:03.703Z","etag":null,"topics":["http","javascript","js","node","nodejs","rpc","rrpc","socket","tcp","websockets"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ya-kostik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-27T15:46:55.000Z","updated_at":"2024-01-29T18:33:05.000Z","dependencies_parsed_at":"2023-01-14T21:50:16.107Z","dependency_job_id":null,"html_url":"https://github.com/ya-kostik/small-rpc","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/ya-kostik/small-rpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ya-kostik%2Fsmall-rpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ya-kostik%2Fsmall-rpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ya-kostik%2Fsmall-rpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ya-kostik%2Fsmall-rpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ya-kostik","download_url":"https://codeload.github.com/ya-kostik/small-rpc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ya-kostik%2Fsmall-rpc/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265066119,"owners_count":23706062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","javascript","js","node","nodejs","rpc","rrpc","socket","tcp","websockets"],"created_at":"2024-11-22T04:13:28.654Z","updated_at":"2025-07-12T22:31:56.799Z","avatar_url":"https://github.com/ya-kostik.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Small RPC\n\nПростой RPC для проекта. Можно использовать как с HTTP, так и с сокетами, так и с любым другим транспортом.\nВытащен и допилен из RPC Redbone, работает на одном с ним протоколе.\n\n## Установка\n```sh\nnpm install small-rpc\n```\nили через Yarn\n```sh\nyarn add small-rpc\n```\n\n## Использование на сервере\nВажно понимать, что библиотека использует `async`/`await` и вам понадобится NodeJS версии 8 или выше.\n### Подключение\n```javascript\nconst RPC = require('small-rpc');\nconst rpc = new RPC();\n\n// Добавляем объект для вызова (module):\nrpc.setModule('profile', new Profile());\n```\n\n```javascript\nconst { json, send } = require('micro');\n// вызов механизма rpc, например, внутри модуля micro\nmodule.exports = async (req, res) =\u003e {\n  const action = await json(req, { limit = '0.3mb', encoding = 'utf8' });\n  try {\n    send(res, 200, await rpc.call({ req, res }, action));\n  } catch(err) {\n    send(req, 500, 'Internal Server Error');\n  }\n};\n```\n\n### Валидация `action`\nRPC валидирует `action` самостоятельно, и по-умолчанию стреляет обычным `Error`, если с `action` что-то не так.\n\nЧтобы более точно отлавливать такие ошибки можно использовать свой наследник от `Error`.\n\n```javascript\nclass MyError extends Error {};\n\nRPC.Error = MyError;\n\nconst { json, send } = require('micro');\nmodule.exports = async (req, res) =\u003e {\n  const action = await json(req, { limit = '0.3mb', encoding = 'utf8' });\n  try {\n    return send(res, 200, await rpc.call({ req, res }, action));\n  } catch(err) {\n    if (err.constructor === MyError) {\n      return send(req, 400, MyError.message);\n    }\n    return send(req, 500, 'Internal Server Error');\n  }\n};\n```\n\n### Добавление библиотеки модулей\nВы можете добавлять объекты пачками, и разделять их на библиотеки.\n```javascript\nconst RPC = require('small-rpc');\nconst rpc = new RPC();\n\n// Добавляем модели mongoose в RPC\nrpc.setLib('mongoose', mongoose.models);\n```\n\nВы также можете дополнить любую библиотеку еще одним модулем:\n```javascript\n// Добавит модуль Profile в библиотеку mongoose\nrpc.setModule('mongoose.Profile', Profile);\n```\n\nЕсли вы добавляете модуль, без указания библиотеки, он добавляется в библиотеку по-умолчанию — `main`.\nТакже если вызвать `setLib` с одним аргументом, и передать просто объект он будет записан как библиотека `main`.\n\n```javascript\n// Записываем модели mongoose как библиотеку `main`\nrpc.setLib(mongoose.models);\n\n// Добавляем объект для вызова profile (из которого мы будем вызывать методы) в библиотеку `main`\nrpc.setModule('profile', new Profile());\n```\n\n### Middleware\nВы можете использовать `middleware`-функции, чтобы определять уровни доступа и дополнительную бизнес-логику.\n\n`Middleware` бывают четырех типов:\n1. Все запросы\n2. Для конкретной библиотеки\n3. Для конкретного модуля\n4. Для конкретного метода\n\nВсе они задаются через метод `use`\n```javascript\nrpc.use((payload, action) =\u003e {\n  // Выполнится для всех запросов\n});\n\nrpc.use('main', (payload, action) =\u003e {\n  // Выполнится для всех запросов к библиотеке `main`\n});\n\nrpc.use('mongoose', (payload, action) =\u003e {\n  // Выполнится для всех запросов к библиотеке `mongoose`\n});\n\nrpc.use('mongoose.Profile', (payload, action) =\u003e {\n  // Выполнится для всех запросов к библиотеке `mongoose` и модулю `Profile`\n});\n\nrpc.use('mongoose.Profile.login', (payload, action) =\u003e {\n  // Выполнится для всех запросов к библиотеке `mongoose` и модулю `Profile` при вызове метода `login`\n});\n```\n\nПри вызове в `middleware` передается:\n`payload` — формируется при вызове `rpc`\n`action` — объект действия `rpc`\n`rpc` — экземляр `rpc` который вызывает `middleware`\n\n\nЧтобы остановить поток выполнения `middleware` нужно просто вернуть `false` из той `middleware` на которой вы хотите остановиться.\n\n\n```javascript\nrpc.use((payload, action) =\u003e {\n  if (!action.jwt) return false;\n});\n```\n\nТакже `middleware` могут быть объектами. Чтобы использовать такую `middleware` нужно определить в объекте метод `call`, аналогичный функциональной версии.\nЭто может быть полезно, если `middleware` большая, и её хочется разделить на части, или если у нее есть параметры и состояние.\n\n### Middleware до и после выполнения метода\nВсе `middleware`, которые вы добавляете методом `use` выполняются до выполнения метода.\nЧтобы добавить немного семантики в код их подключения можно использовать поле `before`.\n```javascript\nrpc.before.use(someMiddleware);\n```\nУ `rpc.before.use` такой же интерфейс, как и у `use`.\n\nЕсли вам необходимо выполнить какую-то проверочную логику после того, как  метод отработал, то для этого используется `rpc.after.use`. Такие middleware будут выполняться после того, как метод отработал и был сформирован итоговый `action`. Интерфейс добавления `after-middleware` такой же как и у обычных `middleware`, за исключением того, что четвертым аргументом в нее передается подготовленный итоговый `action`.\n```javascript\nrpc.after.use(someAfterMiddleware);\n```\nОстановка в такой `middleware` приводит к тому, что все дальнейшие `middleware` добавленные в `after` не будут выполнены, а из `rcp.call` вернется `undefined` вместо `action`.\nЭто отличное место, чтобы добавить реакции на возвращаемые данные, или обогатить `action`, или записать что-то в сессию клиента.\n\n## Возвращаемый `action`\n\nПосле того как механизмы RPC отработают, метод `call` вернет специальный объект — `action`, который можно сразу отправить клиенту. Его анатомия:\n```javascript\n{\n  \"type\": \"@@client/rpc/RETURN\",\n  \"id\": \"ID от запроса, если он был\",\n  \"payload\": \"То что вернул метод\"\n}\n```\n\nЕсли во время выполнения `middleware` были остановлены, то `call` вернет `undefined`, вместо готового `action`.\n\nЛюбой метод из модуля будет вызван с `await`,  то есть методы могут возвращать `Promise` и в ответ попадет результат его штатного разрешения.\n\nЕсли вы хотите, чтобы при возникновении ошибок автоматически генерировался возвращаемый `action`, используйте `safeCall` вместо обычного `call`. В таком случае при возникновении ошибки, которую можно перехватить, т. ч. асинхронной, будет сгенерирован `action`:\n```javascript\n{\n  \"type\": \"@@client/rpc/ERROR\",\n  \"id\": \"ID от запроса, если он был\",\n  \"payload\": {\n    \"message\": \"Сообщение ошибки\",\n    \"code\": \"Eсли у ошибки был код, то он тут будет\"\n  }\n}\n```\n\u003e `type` — может измениться в зависимости от входящего `action`.\n\nВы можете переопределить метод генерации ответов, для этого нужно заменить методы:\n- `makeOutAction(result, inAction)` — для успешных вызовов\n- `makeErrorAction(error, inAction)` — если возникла ошибка\n\n\n## Анатомия входящего `action`\n```json\n{\n  \"type\": \"@@service/rpc/CALL\",\n  \"id\": \"F12AE83\",\n  \"lib\": \"main\",\n  \"module\": \"main\",\n  \"method\": \"echo\",\n  \"arguments\": [\"Hello Redbone RPC\"],\n  \"flat\": true,\n  \"backType\": null,\n  \"merge\": false,\n  \"filter\": null,\n  \"errorType\": null\n}\n```\n\n- `id` — запроса, задается клиентом. Будет прикладываться ко всем ответам протокола. Допустима строка до 24 символов, можно передать число, но в ответе, оно будет передано как строка.\n- `lib` — имя библиотеки объектов, `main` — библиотека по-умолчанию\n- `module` — имя модуля в библиотеке — `main`  — модуль по-умолчанию\n- `method` — имя метода в библиотеке — обязательное поле\n- `arguments` — любое значение которое будет передано в качестве аргумента метода, который будет вызван\n- `flat` — если в `arguments` передать массив, а `flat` задать как `true`, то массив будет разложен по аргументам метода\n- `backType` — тип который будет передан в ответном `action`, если `null` или поле не задано, то будет использован тип по-умолчанию\n- `merge` — если передать как `true`, то ответные данные будут лежать в корне действия, а не поля `payload`. Учтите, что `type` и `id` могут быть перекрыты в этом случае. Если результатом выполнения метода оказался не объект, то `merge` не будет иметь действия, и данные все равно будут находиться внутри поля `payload`\n- `filter` — массив полей, которые нужно вернуть, если в ответе от метода был получен объект\n- `errorType` — тип который будет передан в ответном `action` когда произойдет ошибка вызова (catch любой ошибки). Если задать как `null` будет использован тип по-умолчанию. (Только при `safeCall` или ручном модерировании ошибок).\n\n## Middleware из коробки\n\nДля удобства в директории `/middlewares` есть несколько готовых `middleware` (пока только одна).\n\n### Whitelist\n\nБелый список: ограничивает библиотеки/модули/методы, который могут быть вызваны.\n\nЧтобы её использовать, нужно создать экземляр класса, и передать в качестве аргумента список доступных методов:\n```javascript\nconst whitelist = new Whitelist([\n  'main.mail.send',\n  'main.mail.get',\n  'main.filters',\n  'db.logs',\n  'monitoring'\n]);\n// Подключаем middleware\nrpc.use(whitelist);\n```\n\nСписок можно подключать не только массивом, но и объектом:\n```javascript\nconst whitelist = new Whitelist({\n  main: {\n    mail: {\n      send: true,\n      get: true\n    },\n    filters: true\n  },\n  db: {\n    logs: true\n  },\n  monitoring: true\n});\n// Подключаем middleware\nrpc.use(whitelist);\n```\n\nЕсли очень хочется, то можно задать и через Map:\n```javascript\nconst whitelist = new Whitelist(\n  new Map([\n    ['main.mail.send', true],\n    ['main.mail.get', true],\n    ['main.filters', true],\n    ['db.logs', true],\n    ['monitoring', true]\n  ])\n);\n// Подключаем middleware\nrpc.use(whitelist);\n```\n\nКогда белый список формируется в виде объекта или словаря, в качестве значений узлов можно использовать функции.\nЭти функции будут вызваны как `middleware`, а результат их выполнения будет использоваться для определения доступен метод/модуль/библиотека или нет.\n**Важно**: такая функция должна возвращать булево значение, или оно будет приведено, то есть `undefined` будет расцениваться как `false` в `middleware`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fya-kostik%2Fsmall-rpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fya-kostik%2Fsmall-rpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fya-kostik%2Fsmall-rpc/lists"}