{"id":13485197,"url":"https://github.com/gajus/graphql-lazyloader","last_synced_at":"2025-06-15T11:08:15.230Z","repository":{"id":52556380,"uuid":"312464222","full_name":"gajus/graphql-lazyloader","owner":"gajus","description":"GraphQL directive that adds Object-level data resolvers.","archived":false,"fork":false,"pushed_at":"2021-11-18T18:03:39.000Z","size":33,"stargazers_count":89,"open_issues_count":3,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-15T19:43:56.596Z","etag":null,"topics":["graphql"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gajus.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":"2020-11-13T03:34:02.000Z","updated_at":"2024-07-08T09:08:18.000Z","dependencies_parsed_at":"2022-09-04T14:01:15.144Z","dependency_job_id":null,"html_url":"https://github.com/gajus/graphql-lazyloader","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/gajus/graphql-lazyloader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gajus%2Fgraphql-lazyloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gajus%2Fgraphql-lazyloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gajus%2Fgraphql-lazyloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gajus%2Fgraphql-lazyloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gajus","download_url":"https://codeload.github.com/gajus/graphql-lazyloader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gajus%2Fgraphql-lazyloader/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259964330,"owners_count":22938724,"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":["graphql"],"created_at":"2024-07-31T17:01:50.279Z","updated_at":"2025-06-15T11:08:15.204Z","avatar_url":"https://github.com/gajus.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# graphql-lazyloader 🛋\n\n[![Travis build status](https://img.shields.io/travis/com/gajus/graphql-lazyloader?style=flat-square)](https://travis-ci.com/gajus/graphql-lazyloader)\n[![Coveralls](https://img.shields.io/coveralls/gajus/graphql-lazyloader.svg?style=flat-square)](https://coveralls.io/github/gajus/graphql-lazyloader)\n[![NPM version](http://img.shields.io/npm/v/graphql-lazyloader.svg?style=flat-square)](https://www.npmjs.org/package/graphql-lazyloader)\n[![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical)\n[![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social\u0026label=Follow)](https://twitter.com/kuizinas)\n\nGraphQL directive that adds Object-level data resolvers.\n\n* [Motivation](#motivation)\n* [Usage](#usage)\n  * [Usage Example](#usage-example)\n\n## Motivation\n\nSeveral years ago I read [GraphQL Resolvers: Best Practices](https://medium.com/paypal-engineering/graphql-resolvers-best-practices-cd36fdbcef55) (2018), an article written by PayPal team, that changed my view about where / when data resolution should happen.\n\nLet's start with an example GraphQL schema:\n\n```graphql\ntype Query {\n  person(id: ID) Person!\n}\n\ntype Person {\n  id: ID!\n  givenName: String!\n  familyName: String!\n}\n\n```\n\nA typical GraphQL server uses \"top-heavy\" (parent-to-child) resolvers, i.e. in the above example, `Query.person` is responsible for fetching data for `Person` object. It may look something like this:\n\n```js\n{\n  Query: {\n    person: (root, args) =\u003e {\n      return getPerson(args.id);\n    },\n  },\n};\n\n```\n\nPayPal team argues that this pattern is prone to data over-fetching. Instead, they propose to move data fetching logic to _every_ field of `Person`, e.g.\n\n```js\n{\n  Query: {\n    person: (root, args) =\u003e {\n      return {\n        id: args.id,\n      };\n    },\n  },\n  Person: {\n    givenName: async ({id}) =\u003e {\n      const {\n        givenName,\n      } = await getPerson(id);\n\n      return givenName;\n    },\n    familyName: async ({id}) =\u003e {\n      const {\n        familyName,\n      } = await getPerson(id);\n\n      return givenName;\n    },\n  },\n};\n\n```\n\nIt is important to note that the above example assume that `getPerson` is implemented using a [DataLoader](https://github.com/graphql/dataloader) pattern, i.e. data is fetched only once.\n\nAccording to the original authors, this pattern is better because:\n\n\u003e * This code is easy to reason about. You know exactly where [givenName] is fetched. This makes for easy debugging.\n\u003e * This code is more testable. You don't have to test the [person] resolver when you really just wanted to test the [givenName] resolver.\n\u003e\n\u003e To some, the [getPerson] duplication might look like a code smell. But, having code that is simple, easy to reason about, and is more testable is worth a little bit of duplication.\n\nFor this and other reasons, I became a fan ❤️ of this pattern and have since implemented it in multiple projects. However, the particular implementation proposed by PayPal is pretty verbose. `graphql-lazyloader` abstracts the above logic into a single [GraphQL middleware](https://github.com/maticzav/graphql-middleware) (see [Usage Example](#usage-example)).\n\n## Usage\n\n`graphql-lazyloader` is added using `graphql-middleware`\n\n### Usage Example\n\n```js\nimport {\n  ApolloServer,\n  gql,\n} from 'apollo-server';\nimport {\n  makeExecutableSchema,\n} from '@graphql-tools/schema';\nimport {\n  applyMiddleware,\n} from 'graphql-middleware';\nimport {\n  createLazyLoadMiddleware,\n} from 'graphql-lazyloader';\n\nconst lazyLoadMiddleware = createLazyLoadMiddleware({\n  Person: ({id}) =\u003e {\n    return getPerson(id);\n  },\n});\n\nconst typeDefs = gql`\n  type Query {\n    person(id: ID!): Person!\n  }\n\n  type Person {\n    id: ID!\n    givenName: String!\n    familyName: String!\n  }\n`;\n\nconst resolvers = {\n  Query: {\n    person: () =\u003e {\n      return {\n        id: '1',\n      };\n    },\n  },\n};\n\nconst schema = makeExecutableSchema({\n  resolvers,\n  typeDefs,\n});\n\nconst schemaWithMiddleware = applyMiddleware(\n  schema,\n  lazyLoadMiddleware,\n);\n\nconst server = new ApolloServer({\n  schema: schemaWithMiddleware,\n});\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgajus%2Fgraphql-lazyloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgajus%2Fgraphql-lazyloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgajus%2Fgraphql-lazyloader/lists"}