Skip to content

Selection set for nested object with batchDelegateToSchema #4898

@justbaum30

Description

@justbaum30

Is your feature request related to a problem? Please describe.
Unable (or do not know how) to specify a selectionSet on a subschema when using batchDelegateToSchema (not sure about delegateToSchema yet). In my specific situation, I need to implement a custom valuesFromResults which requires a specific field to be present on the results, regardless if the client requested that field or not. I will provide an example below.

Describe the solution you'd like
On the selection set string, I was expecting to be able to select sub-objects without having to build the entire SelectionSetNode. If I did need to build the node, I was unable to find a straightforward way of dynamically building that in combination with what the client is requesting.

Describe alternatives you've considered
I looked into implementing selectionSet on batchDelegateToSchema, but it seemed pretty burdensome to have to build the full SelectionSetNode object (combining what is always required with what the client requested). It is possible there is a simple way of doing this that I was unable to locate.

Additional context

Code Sample showcasing this. Querying allPosts without requesting user.id should replicate the issue.

import { batchDelegateToSchema } from '@graphql-tools/batch-delegate';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { stitchSchemas } from '@graphql-tools/stitch';
import { OperationTypeNode } from 'graphql';

interface Post {
  id: string;
  text: string;
  userId: string;
  user: User;
}

interface User {
  id: string;
  email: string;
}

const postSchema = makeExecutableSchema({
  typeDefs: /* GraphQL */ `
    type Post {
      id: ID!
      text: String
      userId: ID!
    }

    type Query {
      allPosts: [Post]
    }
  `,
  resolvers: {
    Query: {
      allPosts() {
        return [
          {
            id: '1',
            text: 'my post 1',
            userId: 'userId1',
          },
          {
            id: '2',
            text: 'my post 2',
            userId: 'userId2',
          },
        ];
      },
    },
  },
});

const userSchema = makeExecutableSchema({
  typeDefs: /* GraphQL */ `
    type User {
      id: ID!
      email: String
    }

    type Query {
      usersByIds(ids: [ID!]!): [User]
    }
  `,
  resolvers: {
    Query: {
      usersByIds(_, args) {
        return args.ids
          .map((id: string) => {
            return {
              id: id,
              email: `${id}@email.com`,
            };
          })
          .reverse(); // Reversing so a valuesFromResults is required to implement
      },
    },
  },
});

// setup subschema config objects
const postsSubschema = { schema: postSchema };
const usersSubschema = { schema: userSchema };

export const experimentSchema = stitchSchemas({
  subschemas: [postsSubschema, usersSubschema],
  typeDefs: /* GraphQL */ `
    extend type Post {
      user: User!
    }
  `,
  resolvers: {
    Post: {
      user: {
        selectionSet: `{ userId }`, // Thought I could maybe write { userId user { id} } to force "id" to be requested
        resolve(post: Post, args, context, info) {
          return batchDelegateToSchema({
            schema: usersSubschema,
            operation: OperationTypeNode.QUERY,
            fieldName: 'usersByIds',
            key: post.userId,
            argsFromKeys: (ids) => ({ ids }),
            valuesFromResults(results: User[], keys) {
              return keys.map((key) =>
                results.find((result) => result.id === key) // "id" is required, regardless if client requests it
              );
            },
            context,
            info,
          });
        },
      },
    },
  },
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions