{"id":13998477,"url":"https://github.com/maraisr/dldr","last_synced_at":"2025-04-09T15:04:59.962Z","repository":{"id":142340199,"uuid":"613196706","full_name":"maraisr/dldr","owner":"maraisr","description":"🧩 A tiny/fast dataloader implementation","archived":false,"fork":false,"pushed_at":"2024-10-20T23:36:06.000Z","size":143,"stargazers_count":31,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-09T15:04:54.683Z","etag":null,"topics":["batch","dataloader"],"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/maraisr.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},"funding":{"github":"maraisr"}},"created_at":"2023-03-13T05:01:11.000Z","updated_at":"2024-10-25T10:49:03.000Z","dependencies_parsed_at":"2024-10-22T02:55:50.107Z","dependency_job_id":null,"html_url":"https://github.com/maraisr/dldr","commit_stats":{"total_commits":88,"total_committers":2,"mean_commits":44.0,"dds":"0.011363636363636354","last_synced_commit":"614317540d4982b779a3756a560a96b74ec2060f"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fdldr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fdldr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fdldr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fdldr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maraisr","download_url":"https://codeload.github.com/maraisr/dldr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"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":["batch","dataloader"],"created_at":"2024-08-09T19:01:42.279Z","updated_at":"2025-04-09T15:04:59.936Z","avatar_url":"https://github.com/maraisr.png","language":"TypeScript","funding_links":["https://github.com/sponsors/maraisr","https://www.buymeacoffee.com/marais"],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cdiv align=\"left\"\u003e\n\n\u003csamp\u003e\n\n# dldr [![licenses](https://licenses.dev/b/npm/dldr?style=dark)](https://licenses.dev/npm/dldr)\n\n\u003c/samp\u003e\n\n**A tiny utility for batching and caching operations**\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\u003csup\u003e\n\nThis is free to use software, but if you do like it, consisder supporting me ❤️\n\n[![sponsor me](https://badgen.net/badge/icon/sponsor?icon=github\u0026label\u0026color=gray)](https://github.com/sponsors/maraisr)\n[![buy me a coffee](https://badgen.net/badge/icon/buymeacoffee?icon=buymeacoffee\u0026label\u0026color=gray)](https://www.buymeacoffee.com/marais)\n\n\u003c/sup\u003e\n\n\u003c/div\u003e\n\n## ⚙️ Install\n\n- **npm** — available as [`dldr`](https://www.npmjs.com/package/dldr)\n- **JSR** — available as [`@mr/dataloader`](https://jsr.io/@mr/dataloader)\n\n## 🚀 Usage\n\nThe default module will batch calls to your provided `loadFn` witin the current tick.\n\nUnder the hood we schedule a function with\n[`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). That then calls\nyour `loadFn` with the unique keys that have been requested.\n\n```ts\nimport { load } from 'dldr';\n\n// ⬇️ define some arbitary load method that accepts a single argument array of keys\nconst getPosts = (keys) =\u003e sql`SELECT id, name FROM posts WHERE id IN (${keys})`;\n\n// .. for convenience, you could bind\nconst loadPost = load.bind(null, getPosts);\n\n// ⬇️ demo some collection that is built up over time.\nconst posts = [\n\tload(getPosts, '123'),\n\tloadPost('123'), // functionally equivalent to the above\n\tload(getPosts, '456'),\n];\n\n// ...\n\nposts.push(load(getPosts, '789'));\n\n// ⬇️ batch the load calls, and wait for them to resolve\nconst loaded = await Promise.all(posts);\n\nexpect(getPosts).toHaveBeenCalledWith(['123', '456', '789']);\nexpect(loaded).toEqual([\n\t{ id: '123', name: '123' },\n\t{ id: '123', name: '123' },\n\t{ id: '456', name: '456' },\n\t{ id: '789', name: '789' },\n]);\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eGraphQL Resolver Example\u003c/summary\u003e\n\n```ts\nimport { load } from 'dldr';\nimport { buildSchema, graphql } from 'graphql';\n\nconst schema = buildSchema(`\n    type Query {\n        me(name: String!): String!\n    }\n`);\n\nconst operation = `{\n    a: me(name: \"John\")\n    b: me(name: \"Jane\")\n}`;\n\nconst results = await graphql({\n\tschema,\n\tsource: operation,\n\tcontextValue: {\n\t\tgetUser: load.bind(null, async (names) =\u003e {\n\t\t\t// Assume youre calling out to a db or something\n\t\t\tconst result = names.map((name) =\u003e name);\n\n\t\t\t// lets pretend this is a promise\n\t\t\treturn Promise.resolve(result);\n\t\t}),\n\t},\n\trootValue: {\n\t\tme: ({ name }, ctx) =\u003e {\n\t\t\treturn ctx.getUser(name);\n\t\t},\n\t},\n});\n```\n\n\u003c/details\u003e\n\n### Caching\n\nOnce a key has been loaded, it will be cached for all future calls.\n\n```ts\nimport { load } from 'dldr/cache';\nimport { getPosts } from './example';\n\n// operates the same as the above, but will cache the results of the load method\n\nconst cache = new Map();\nconst loadPost = load.bind(null, getPosts, cache);\n// note; cache is optional, and will be created if not provided\n\nconst posts = Promise.all([\n\tload(getPosts, cache, '123'),\n\tloadPost('123'), // will be cached, and functionally equivalent to the above\n\tloadPost('456'),\n]);\n\nexpect(getPosts).toHaveBeenCalledTimes(1);\nexpect(getPosts).toHaveBeenCalledWith(['123', '456']);\nexpect(loaded).toEqual([\n\t{ id: '123', name: '123' },\n\t{ id: '123', name: '123' },\n\t{ id: '456', name: '456' },\n]);\n\n// ⬇️ the cache will be used for subsequent calls\nconst post = await loadPost('123');\n\nexpect(getPosts).toHaveBeenCalledTimes(1); // still once\nexpect(post).toEqual({ id: '123', name: '123' });\n```\n\n### API\n\n#### Module: `dldr`\n\nThe main entry point to start batching your calls.\n\n\u003c!-- prettier-ignore-start --\u003e\n\n```ts\nfunction load\u003cT\u003e(\n\tloadFn: (keys: string[]) =\u003e Promise\u003c(T | Error)[]\u003e,\n\tkey: string,\n\tidentityKey?: string,\n): Promise\u003cT\u003e;\n```\n\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003e **Note** Might be worth calling `.bind` if you dont want to pass your loader everywhere.\n\u003e\n\u003e ```js\n\u003e const userLoader = load.bind(null, getUsers);\n\u003e\n\u003e await userLoader('123');\n\u003e ```\n\n#### Module: `dldr/cache`\n\nA submodule that will cache the results of your `loadFn` between ticks.\n\n```ts\nfunction load\u003cT\u003e(\n\tloadFn: (keys: string[]) =\u003e Promise\u003c(T | Error)[]\u003e,\n\tcache: MapLike\u003cstring, T\u003e | undefined,\n\tkey: string,\n\tidentityKey?: string,\n): Promise\u003cT\u003e;\n```\n\n\u003e A default `Map` based `cache` will be used if you dont provide one.\n\n**_Self managed cache_**\n\nWe explicitly do not handle mutations, so if you wish to retrieve fresh entries, or have a primed\ncache we recommend you do so yourself. All we require is a `Map` like object.\n\nCommonly an LRU cache is used, we recommend [`tmp-cache`](https://github.com/lukeed/tmp-cache).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport LRU from 'tmp-cache';\nimport { load } from 'dldr/cache';\n\nconst loadUser = load.bind(null, getUsers, new LRU(100));\n```\n\n\u003c/details\u003e\n\n## License\n\nMIT © [Marais Rossouw](https://marais.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaraisr%2Fdldr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaraisr%2Fdldr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaraisr%2Fdldr/lists"}