Skip to content

Instantly share code, notes, and snippets.

@loganpowell
Last active June 8, 2023 17:51
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save loganpowell/56af8f0e2b68af21f88833075092f864 to your computer and use it in GitHub Desktop.
Save loganpowell/56af8f0e2b68af21f88833075092f864 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