Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gajus/graphql-lazyloader
GraphQL directive that adds Object-level data resolvers.
https://github.com/gajus/graphql-lazyloader
graphql
Last synced: 12 days ago
JSON representation
GraphQL directive that adds Object-level data resolvers.
- Host: GitHub
- URL: https://github.com/gajus/graphql-lazyloader
- Owner: gajus
- License: other
- Created: 2020-11-13T03:34:02.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2021-11-18T18:03:39.000Z (almost 3 years ago)
- Last Synced: 2024-10-26T12:46:04.454Z (18 days ago)
- Topics: graphql
- Language: TypeScript
- Homepage:
- Size: 32.2 KB
- Stars: 89
- Watchers: 6
- Forks: 5
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - graphql-lazyloader - level data resolvers. | gajus | 86 | (TypeScript)
README
# graphql-lazyloader 🛋
[![Travis build status](https://img.shields.io/travis/com/gajus/graphql-lazyloader?style=flat-square)](https://travis-ci.com/gajus/graphql-lazyloader)
[![Coveralls](https://img.shields.io/coveralls/gajus/graphql-lazyloader.svg?style=flat-square)](https://coveralls.io/github/gajus/graphql-lazyloader)
[![NPM version](http://img.shields.io/npm/v/graphql-lazyloader.svg?style=flat-square)](https://www.npmjs.org/package/graphql-lazyloader)
[![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical)
[![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas)GraphQL directive that adds Object-level data resolvers.
* [Motivation](#motivation)
* [Usage](#usage)
* [Usage Example](#usage-example)## Motivation
Several 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.
Let's start with an example GraphQL schema:
```graphql
type Query {
person(id: ID) Person!
}type Person {
id: ID!
givenName: String!
familyName: String!
}```
A 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:
```js
{
Query: {
person: (root, args) => {
return getPerson(args.id);
},
},
};```
PayPal 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.
```js
{
Query: {
person: (root, args) => {
return {
id: args.id,
};
},
},
Person: {
givenName: async ({id}) => {
const {
givenName,
} = await getPerson(id);return givenName;
},
familyName: async ({id}) => {
const {
familyName,
} = await getPerson(id);return givenName;
},
},
};```
It 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.
According to the original authors, this pattern is better because:
> * This code is easy to reason about. You know exactly where [givenName] is fetched. This makes for easy debugging.
> * This code is more testable. You don't have to test the [person] resolver when you really just wanted to test the [givenName] resolver.
>
> 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.For 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)).
## Usage
`graphql-lazyloader` is added using `graphql-middleware`
### Usage Example
```js
import {
ApolloServer,
gql,
} from 'apollo-server';
import {
makeExecutableSchema,
} from '@graphql-tools/schema';
import {
applyMiddleware,
} from 'graphql-middleware';
import {
createLazyLoadMiddleware,
} from 'graphql-lazyloader';const lazyLoadMiddleware = createLazyLoadMiddleware({
Person: ({id}) => {
return getPerson(id);
},
});const typeDefs = gql`
type Query {
person(id: ID!): Person!
}type Person {
id: ID!
givenName: String!
familyName: String!
}
`;const resolvers = {
Query: {
person: () => {
return {
id: '1',
};
},
},
};const schema = makeExecutableSchema({
resolvers,
typeDefs,
});const schemaWithMiddleware = applyMiddleware(
schema,
lazyLoadMiddleware,
);const server = new ApolloServer({
schema: schemaWithMiddleware,
});```