{"id":30267134,"url":"https://github.com/xxshady/altv-xrpc","last_synced_at":"2025-08-15T23:28:11.728Z","repository":{"id":64181095,"uuid":"401798414","full_name":"xxshady/altv-xrpc","owner":"xxshady","description":"alt:V type-safe request-response events library for TypeScript users","archived":false,"fork":false,"pushed_at":"2023-09-07T19:33:40.000Z","size":177,"stargazers_count":14,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-07-30T16:53:25.556Z","etag":null,"topics":["altv","rpc"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xxshady.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-31T18:02:35.000Z","updated_at":"2024-03-28T12:36:36.000Z","dependencies_parsed_at":"2023-01-15T03:00:35.492Z","dependency_job_id":null,"html_url":"https://github.com/xxshady/altv-xrpc","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/xxshady/altv-xrpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xxshady%2Faltv-xrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xxshady%2Faltv-xrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xxshady%2Faltv-xrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xxshady%2Faltv-xrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xxshady","download_url":"https://codeload.github.com/xxshady/altv-xrpc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xxshady%2Faltv-xrpc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270644764,"owners_count":24621332,"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","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["altv","rpc"],"created_at":"2025-08-15T23:27:12.543Z","updated_at":"2025-08-15T23:28:11.716Z","avatar_url":"https://github.com/xxshady.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# altv-xrpc\n\nSpecial thanks ❤️ to [innxz](https://github.com/innxz) for financially supporting me and this library\n\n![Code_8rnWxzdSIY](https://user-images.githubusercontent.com/54737754/208540996-e7862b93-2b85-4d4d-9217-a68924d0b50d.gif)\n\naltv-xrpc is a library that implements **strict** request-response events (rpc) and made for *real* TypeScript users\n\n## Installation\n\n### yarn\n\n```\nyarn add altv-xrpc-server\nyarn add altv-xrpc-client\nyarn add altv-xrpc-webview\nyarn add altv-xrpc-shared\nyarn add altv-xrpc-shared-types  // advanced typescript support\n```\n\n### npm\n\n```\nnpm install altv-xrpc-server\nnpm install altv-xrpc-client\nnpm install altv-xrpc-webview\nnpm install altv-xrpc-shared\nnpm install altv-xrpc-shared-types // advanced typescript support\n```\n\n## Usage\n\nMini-example of advanced typescript support (`altv-xrpc-shared-types`) usage is [here](/example)\n\n### client\n\n```ts\nimport * as alt from \"alt-client\"\nimport { rpc } from \"altv-xrpc-client\"\n\n// client \u003c-\u003e server\nrpc.onServer(\"example\", (data) =\u003e data)\nrpc.onServer(\"example\" (data) =\u003e 123) // error, one rpc can only have one listener\nrpc.offServer(\"example\") // remove listener\nrpc.emitServer(\"example\", 123)\n  .then(result =\u003e console.log(\"server rpc result:\", result))\n  \n  // possible reasons of rejection:\n  // listener is running too long (more than 15 seconds) (code: Expired)\n  // listener is not added for that rpc name (code: HandlerNotRegistered)\n  // this rpc is already running and no response has been received yet (code: AlreadyPending)\n  .catch(e =\u003e console.error(\"something went wrong\", e.stack)\n\n// client \u003c-\u003e webview\nrpc.useWebView(new alt.WebView(...)) // only one WebView instance can be used\n\nrpc.onWebView(\"example\", (data) =\u003e data)\nrpc.emitWebView(\"example\", 123)\n  .then(result =\u003e console.log(\"webview rpc result:\", result))\n  .catch(e =\u003e console.error(\"something went wrong\", e.stack)\n```\n\n### server\n\n```ts\nimport * as alt from \"alt-server\"\nimport { rpc } from \"altv-xrpc-server\"\n\n// server \u003c-\u003e client\nrpc.onClient(\"example\", (data) =\u003e data)\nrpc.emitClient(alt.Player.all[0], \"example\", 123)\n  .then(result =\u003e alt.log(\"client rpc result:\", result))\n\n  // in addition to all of the above reasons of rejection, here the player can disconnect (code: PlayerDisconnected)\n  .catch(e =\u003e alt.logError(\"something went wrong\", e.stack))\n\n// server \u003c-\u003e webview\nrpc.onWebView(\"example\", (data) =\u003e data)\n\n// rpc event will be send to webview that was added using rpc.useWebView on client-side\nrpc.emitWebView(alt.Player.all[0], \"example\", 123)\n  .then(result =\u003e alt.log(\"webview rpc result:\", result))\n  // possible rejection: rpc.useWebview is not used on client-side (WebViewNotAdded)\n  .catch(e =\u003e alt.logError(\"something went wrong\", e.stack))\n```\n\n### webview\n\n```ts\nimport { rpc } from \"altv-xrpc-webview\"\n\nrpc.onClient(\"example\", (data) =\u003e data)\nrpc.emitClient(\"example\", 123)\n  .then(result =\u003e console.log(\"client rpc result:\", result))\n  .catch(e =\u003e console.error(\"something went wrong\", e.stack))\n\nrpc.onServer(\"example\", (data) =\u003e data)\nrpc.emitServer(\"example\", 123)\n  .then(result =\u003e console.log(\"server rpc result:\", result))\n  .catch(e =\u003e console.error(\"something went wrong\", e.stack))\n```\n\n## Custom events API\n\nBy default this library uses alt:V events API (`alt.emitServer`, `alt.onClient`, etc.), but it's possible to override this behavior, for example if you want to add some protection\n\nclient-side\n\n```ts\nimport { Rpc } from \"altv-xrpc-client\"\n\nconst myWebView = new alt.WebView(...)\n\nconst rpc = new Rpc({\n  eventApi: {\n    onServer: (event, handler) =\u003e {\n      alt.log(\"rpc called onServer (add event handler) with params:\", [event, handler])\n      alt.onServer(event, handler)\n    },\n    offServer: (event, handler) =\u003e {\n      alt.log(\"rpc called offServer (remove event handler) with params:\", [event, handler])\n      alt.offServer(event, handler)\n    },\n    emitServer: (event, ...args) =\u003e {\n      alt.log(\"rpc called emitServer with params:\", [event, args])\n      alt.emitServer(event, ...args)\n    },\n  },\n  webView: {\n    on: (event, handler) =\u003e {\n      alt.log(\"rpc called WebView on method (add event handler) with params:\", [event, handler])\n      myWebView.on(event, handler)\n    },\n    emit: (event, ...args) =\u003e {\n      alt.log(\"rpc called WebView emit method with params:\", [event, args])\n      myWebView.emit(event, handler)\n    },\n  }\n})\n\n// Will output to the client console \"rpc called emitServer with params: [\"rpc:callEvent\", [ \"example\", [ 123 ] ] ]\"\nrpc.emitServer(\"example\", 123) \n```\n\nserver-side\n\n```ts\nimport { Rpc } from \"altv-xrpc-server\"\n\nconst rpc = new Rpc({\n  eventApi: {\n    onClient: (event, handler) =\u003e {\n      alt.log(\"rpc called onClient (add event handler) with params:\", [event, handler])\n      alt.onClient(event, handler)\n    },\n    offClient: (event, handler) =\u003e {\n      alt.log(\"rpc called offClient (remove event handler) with params:\", [event, handler])\n      alt.offClient(event, handler)\n    },\n    emitClient: (player, event, ...args) =\u003e {\n      alt.log(\"rpc called emitClient with params:\", [player, event, args])\n      alt.emitClient(player, event, ...args)\n    },\n  }\n})\n\n\nalt.on(\"playerConnect\", (player) =\u003e {\n  rpc.emitClient(player, \"example\", 123)\n  // Output to the server console:\n  // rpc called emitClient with params: [\n  //   Player {},\n  //   \"rpc:callEvent\",\n  //   [ \"test\", [ 123 ] ]\n  // ]\n})\n```\n\n## Server-side hooks API\n\nEasier verification of everything the client (webview) sends to the server (client-\u003eserver rpc call, server-\u003eclient rpc response).\n\nserver-side\n\n```ts\nimport { Rpc } from \"altv-xrpc-server\"\n\nconst myClientServerRpcArgs = {\n  a: \"must be number\",\n  b: \"must be string\",\n}\n\nconst myServerClientResponses = {\n  a: \"must be number\",\n  b: \"must be string\",\n}\n\nconst rpc = new Rpc({\n  hooks: {\n    // server-side rpc.onClient player \u0026 args\n    clientServerCall(player, rpcName, args) {\n      const expected = myClientServerRpcArgs[rpcName as keyof typeof myClientServerRpcArgs]\n      const [value] = args\n      if (expected === \"must be number\" \u0026\u0026 typeof value !== \"number\") {\n        return null // will send `InvalidClientServerArgsOrPlayer` error code to client\n      }\n      if (expected === \"must be string\" \u0026\u0026 typeof value !== \"string\") {\n        return null\n      }\n\n      return {\n        player,\n        args\n      }\n    },\n\n    // server-side rpc.emitClient response (returned value)\n    serverClientResponse(player, rpcName, response) {\n      const expected = myServerClientResponses[rpcName as keyof typeof myServerClientResponses]\n      if (expected === \"must be number\" \u0026\u0026 typeof response !== \"number\") {\n        return null // will reject rpc.emitClient promise with `InvalidServerClientResponse` error code\n      }\n      if (expected === \"must be string\" \u0026\u0026 typeof response !== \"string\") {\n        return null\n      }\n\n      return { response }\n    },\n  }\n})\n\n// serverClientResponse\n// if client sends not string this promise will be rejected\nconst response = await rpc.emitClient(player, \"b\")\n\n```\n\nclient-side (same for webview)\n\n```ts\nimport { rpc } from \"altv-xrpc-client\"\n\n// will fail\n// a: \"must be number\"\nconst response = await rpc.emitServer(\"a\", null)\n```\n\n## WebView reset\n\nFor cases where `AlreadyPending` errors occur during hot reloads in browser.\n\nExample usage for [Vite HMR](https://vitejs.dev/guide/api-hmr.html).\n\n```ts\nimport { rpc } from 'altv-xrpc-webview'\n\nconst hotReload = (import.meta as any).hot\nif (hotReload) {\n  hotReload.on('vite:beforeUpdate', (newModule: unknown) =\u003e {\n    if (!newModule) return\n    rpc.reset()\n  })\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxxshady%2Faltv-xrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxxshady%2Faltv-xrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxxshady%2Faltv-xrpc/lists"}