Skip to content

Instantly share code, notes, and snippets.

@gatherheart
Forked from loganpowell/graphql-args-passing.md
Created November 12, 2020 09:05
Show Gist options
  • Save gatherheart/4a007c105b3a1526ec055c374b782e0f to your computer and use it in GitHub Desktop.
Save gatherheart/4a007c105b3a1526ec055c374b782e0f to your computer and use it in GitHub Desktop.
GraphQL Passing Arguments (parent, args, context, info)

Communicating Between GraphQL Resolver Functions

TODO:

Overview

Graphql-Resolution-Diagram

The Resoultion Steps:

  1. The query arrives at the server.
  2. The server invokes the resolver for the root field user — let’s assume fetchUserById returns this object: { "id": "abc", "name": "Sarah" }
  3. The server invokes the resolver for the field id on the User type. The parent input argument for this resolver is the return value from the previous invocation, so it can simply return parent.id .
  4. Analogous to 3, but returns parent.name in the end. (Note that 3 and 4 can happen in parallel.)
  5. The resolution process is terminated — finally the result gets wrapped with a data field to adhere to the GraphQL spec:

Resources

Using context

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.

Answer from Dan Schafer:

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.

Source References

Githunt Example (from Apollo):

// 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(),
    },
  };
}));

Source References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment