{"id":22970727,"url":"https://github.com/yosbelms/remote-func","last_synced_at":"2025-08-13T11:33:46.724Z","repository":{"id":38272817,"uuid":"226576973","full_name":"yosbelms/remote-func","owner":"yosbelms","description":":fox_face:  JavaScript as the query language for your API","archived":false,"fork":false,"pushed_at":"2023-07-10T02:43:26.000Z","size":1134,"stargazers_count":13,"open_issues_count":9,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-09T16:04:48.842Z","etag":null,"topics":["api","function","graphql","http","nodejs","rest","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yosbelms.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-07T21:15:26.000Z","updated_at":"2023-07-07T23:05:27.000Z","dependencies_parsed_at":"2024-10-28T08:04:38.154Z","dependency_job_id":"bf8c180c-6076-4db6-9541-13bdd3eb6f43","html_url":"https://github.com/yosbelms/remote-func","commit_stats":{"total_commits":222,"total_committers":5,"mean_commits":44.4,"dds":"0.39639639639639634","last_synced_commit":"84555e83bb3cfacc403bc1e6db8487d1b87155e3"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosbelms%2Fremote-func","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosbelms%2Fremote-func/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosbelms%2Fremote-func/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosbelms%2Fremote-func/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yosbelms","download_url":"https://codeload.github.com/yosbelms/remote-func/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229759840,"owners_count":18119874,"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":["api","function","graphql","http","nodejs","rest","typescript"],"created_at":"2024-12-14T22:14:27.145Z","updated_at":"2024-12-14T22:14:27.878Z","avatar_url":"https://github.com/yosbelms.png","language":"TypeScript","readme":"**Work in progress**\n\n# Remote-Func\n### JavaScript as the query language for your API\n\nRemote-func allows use a subset of JavaScript as the query language of your APIs.\n\nExample:\n\n```ts\nconst getBlogEntry = bind(client, func(`async (id) =\u003e {\n  const blog = await blogService.find(id)\n  return {\n    name: blog.name,\n    content: blog.content\n  }\n}`))\n\ngetBlogEntry().then(...)\n```\n\n# Key features\n\n- Twice faster than GraphQL.\n- JavaScript as the query language.\n- Fetch multiple resources at once.\n- Allows to reduce data before sending the response to the client.\n- Batch and deduplicate requests.\n- Stream data from server to client in a single HTTP response, and dispatch in the order of arrival.\n- End-to-end type safety when used with TypeScript.\n- Independence between frontend and backend.\n\n# Documentation\n\n- [Overview](#overview)\n- [Examples](#examples)\n- [Installation](#installation)\n- [Server](#server)\n  - [Services](#services)\n  - [HTTP server](#http-server)\n- [Client](#client)\n  - [HTTP client](#http-client)\n  - [Query](#query)\n  - [Partial Queries](#partial-queries)\n- [Babel plugin](#babel-plugin)\n  - [Configuring](#configuring)\n  - [Type definition import](#type-definition-import)\n  - [Query](#query)\n\n# Overview\n\nRemote-func is a TypeScript library focused on developer experience. There are two primary methods of using Remote-func. The first is for plain JavaScript, the second is for use with Babel (which supports TypeScript) allowing type safety between client and server sides.\n\n# Examples\n\nExamples can be found in [examples](tree/master/examples) directory.\n\n- __Simple:__ Simple examples written using client AMD bundle.\n- __Type safe:__ Show RemoteFunc end-to-end type safety capabilities when combined with TypeScript.\n- __Secured Endpoint:__ Make use of context to add cookie based endpoint security.\n\n# Installation\n\n```\nnpm i remote-func\n```\n\n# Server\n\n## Services\n\nA Remote-func service is a collection of endpoints, there is where the server logic lives. \n\n```ts\nimport { createService  } from '../server'\n\n// service definition\nexport const blogService = createService(ctx =\u003e ({\n  // endpoint definition\n  async find(id: number) {\n    // ...\n  }\n}))\n```\n\n## HTTP Server \n\n```ts\nimport express from 'express'\nimport { expressHandler, createEngine } from '../server'\n\nconst PORT = 5000\nconst app = express()\n\napp.use('/', expressHandler({\n  // engine creation\n  engine: createEngine({\n    // path to the services module\n    servicesPath: './services'\n  })\n}))\n\n// start express\napp.listen(PORT, () =\u003e console.log(`Remote func running on port ${PORT}`))\n```\n\n# Client\n\n## HTTP client\n\n```ts\nimport { createClient, httpHandler } from '../client'\n\nconst client = createClient({\n  // http handler creation\n  handler: httpHandler({\n    url: 'http://localhost:5000/',\n  })\n})\n```\n\n## Query\n\nQueries in Remote-func are just JS code that will be executed by the Remote-func engine. Often composed by a block of requests, and a block of data reductions. Remote functions needs to be bound to a Remote-func client. See: [Query function](/docs/query-function.md)\n\n```ts\nimport { bind, func } from '../client'\n\n// create a query function\nconst getBlogEntry = bind(client, func(`async (id) =\u003e {\n  // request\n  const blog = await blogService.find(id)\n\n  // reduction\n  return {\n    name: blog.name,\n    content: blog.content\n  }\n}`))\n\ngetBlogEntry(5).then(entry =\u003e console.log(entry))\n```\n\n## Partial Queries\n\nRemote-func allow pass queries as parameter of another queries. This feature is useful if you want to reuse logic among different queries.\n\n```ts\nimport { bind } from '../client'\n\n// create reusable query function\n// there is no need to bind here\nconst mapBlogEntry = func(`async (entry) =\u003e {\n  return {\n    name: entry.name,\n    content: entry.content\n  }\n}`)\n\n// create a query function\nconst getBlogEntry = bind(client, func(`async (id, mapBlogEntry) =\u003e {\n  const blog = await blogService.find(id)\n\n  // using the partial query passed by param\n  return mapBlogEntry(blog)\n}`))\n\ngetBlogEntry(5, mapBlogEntry).then(entry =\u003e console.log(entry))\n```\n\n\u003e Gotcha: partial queries does not accept partial queries as param\n\n\n## RPC\n\nRemote-func allow to use services as simple RPC system. This way doesn't evaluete JS code in the server side, but. By using this mode it is not possible to take advantage of the query mode capabilities to avoid over-fetching, and under-fetching.\n\n```ts\nimport { bind } from '../client'\n\n// create a rpc client\nconst blogService = bind(client, 'blogService')\nblogService.find(5).then(entry =\u003e console.log(entry))\n```\n\n\n## Babel plugin\n\nRemote-func bundles a Babel plugin to allow to take advantage IDEs intellisense and type checking.\n\n## Configuring\n\nThe Remote-func plugin must be the first plugin in your Babel config plugins list.\n\n```json\n{\n  \"plugins\": [\n    \"remote-func/dev-tools/babel-plugin\",\n    ...otherPlugins\n  ]\n}\n```\n\nThe plugin can receive some parameters, useful to intercept its transpilation stages. Example usage:\n\n```js\n\"plugins\": [\n  [\"remote-func/dev-tools/babel-plugin\", {\n    test: /\\.ts$/,\n    transpile: src =\u003e src,\n    transform: src =\u003e src,\n  }]\n]\n```\n\nOptions:\n\n- **test**: file path test regex, default /\\.(js|mjs|jsx|ts|tsx)$/\n- **transpile**: transpilation function, default TypeScript\n- **trasform**: receive the final code right before write, the transpiled code\n            will be replaced by the returning value.\n\n## Type definition import\n\nTypeScript needs to know about your services types in order to compile. Remote-func ships a tool to extract type definitions from service source code and generate `.js` stub files to allow TypeScript validate client code.\n\nYou can achieve it by executing the following command:\n\n```ts\nnpx remote-func extract-dts --source='path/to/services.ts' --out='dts/for/client'\n```\n\nAfter run it you should have type descriptors(`.d.ts`) files corresponding to your API module in the specified directory. At this point you can import the services module from the client source code and write queries as if you were accessing endpoints directly from the client source code.\n\n## Query\n\n```ts\nimport { blogService } from 'path/to/blog-api'\n\nconst getBlogEntry = bind(client, func(async (id) =\u003e {\n  const blog = await blogService.find(id)\n  return {\n    name: blog.name,\n    content: blog.content\n  }\n}))\n\ngetBlogEntry(5).then(entry =\u003e console.log(entry))\n```\n\n\u003e __Important:__ the code of your query can only used variables imported from endpoints types and those defined inside of the query.\n\n\n## Batching\n\nBatching allows to obtain the result of many function call in one HTTP call. The server will stream back the results as they are resolved. If the browser supports WebStreams, the HTTP client will resolve each function as soon as each result arrives. If the browser doesn't support WebStream all the functions will resolve when all responses arrived.\n\n```ts\n// begin batch\nclient.useBatch(true)\n\n// enqueue queries\nquery1()\nquery2()\n\n// execute queries\nclient.flush()\n```\n\n`query1`, and `query2` will be executed in one batch request.\n\n__Auto Flus__\n\nIn the following case there is no need to explicitly call `flush` since the client will flush automatically if 5 milliseconds elapsed or after 10 queued requests.\n\n```ts\nclient.useBatch({\n  timeout: 5,\n  sizeLimit: 10,\n})\n```\n\nMIT (c) 2019-present Yosbel Marin\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosbelms%2Fremote-func","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyosbelms%2Fremote-func","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosbelms%2Fremote-func/lists"}