- Read DataLoader Source
- Check out Keechma's Dataloader and Graphql Builder... (video)
- Check out this example with GraphQL + Express + Dataloader
- The query arrives at the server.
- The server invokes the resolver for the root field
user
— let’s assumefetchUserById
returns this object:{ "id": "abc", "name": "Sarah" }
- The server invokes the resolver for the field
id
on theUser
type. Theparent
input argument for this resolver is the return value from the previous invocation, so it can simply returnparent.id
. - Analogous to 3, but returns
parent.name
in the end. (Note that 3 and 4 can happen in parallel.) - The resolution process is terminated — finally the result gets wrapped with a
data
field to adhere to the GraphQL spec:
Github Question
What if the query looks like this?
users {
info {
email {
address
}
}
}
In this case
address
resolver does not know which user it belongs to, because the query is querying for multiple users.
If the ability to see the address depends on whose address it is, I would expect the object returned by email to have a reference to the User; at that point, you would do:
address: {
resolve: (email, _, context) => context.isFriendsWith(email.getUser()) ? email.getAddress() : null
}
Though better than that would be to have that logic live inside the email business object:
class Email {
getAddress(context) {
return context.isFriendsWith(getUser()) ? this.address : null;
}
getUser() {
return this.user; // The email knows about the user because the address visibility depends on it
}
}
address: {
resolve: (email, _, context) => email.getAddress(context)
}
This way, every caller of Email.getAddress() has the desired visibility rules applied.
- Dan Schafer github comment
// from github/models.js
export class Repositories {
constructor({ connector }) {
this.connector = connector;
}
getByFullName(fullName) {
return this.connector.get(`/repos/${fullName}`);
}
}
// from github/connector.js
export class GitHubConnector {
constructor({ clientId, clientSecret } = {}) {
this.clientId = clientId;
this.clientSecret = clientSecret;
// Allow mocking request promise for tests
this.rp = rp;
if (GitHubConnector.mockRequestPromise) {
this.rp = GitHubConnector.mockRequestPromise;
}
this.loader = new DataLoader(this.fetch.bind(this), {
// The GitHub API doesn't have batching, so we should send requests as
// soon as we know about them
batch: false,
});
}
...
// from api/index.js
app.use('/graphql', apolloExpress((req) => {
...
// Initialize a new GitHub connector instance for every GraphQL request, so that API fetches
// are deduplicated per-request only.
const gitHubConnector = new GitHubConnector({
clientId: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET,
});
return {
schema,
context: {
user,
Repositories: new Repositories({ connector: gitHubConnector }),
Users: new Users({ connector: gitHubConnector }),
Entries: new Entries(),
Comments: new Comments(),
},
};
}));
- Apollo Docs: Data Fetching
- Example of using
context
in github
- Example of using