Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fragments in schema directives #984

Closed
lastmjs opened this issue Oct 23, 2018 · 9 comments
Closed

Add fragments in schema directives #984

lastmjs opened this issue Oct 23, 2018 · 9 comments

Comments

@lastmjs
Copy link

lastmjs commented Oct 23, 2018

Right now there doesn't seem to be a way to request additional fields other than what is in the user-defined selection set in a schema directive. It would be very useful to request additional fields that the user did not request, within a schema directive. This would open up more possibilities for directive permissions. Essentially, from within a schema directive implementation, I would like to not only be able to define a custom resolver, but a custom fragment to be applied to the parent when that resolver is invoked at runtime.

@lastmjs
Copy link
Author

lastmjs commented Oct 23, 2018

Yes, it would be perfect if for example I could do something like the following:

class TestDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field, details) {
    field.fragment = `fragment Fragment on ${details.objectType.name} { id }`;
    field.resolve = (parent, args, context, info) => {
      // Do anything you want with parent.id
      // This would be VERY useful for authorization/permissions
      return 'It works';
    };
  }
}

@omnibrain
Copy link

+1 I need exactly this feature please.

Consider the following use case:

Let's say I have a model like this:

type User {
    email: String!
    chatRooms: [ChatRoom!]!
}

type ChatRoom {
    id: ID!
  chatMessage: [Message!]!
}

type Message {
  id: ID!
  chatRoom: ChatRoom!
  content: String!
}

type Mutation {
  deleteMessage(id: ID!): ChatMessage! @isChatRoomMember
}

Now the isChatRoomMember directive needs to know if I'm a member of the chat room, and for that it needs to know the ChatRoom it belongs to. But if the user doesn't request the chatRoom field it will not be available in the isChatRoomMember directive.

The only way I see to implement such a directive is if fragments could be resolved for directives then I could simply make sure the chatRoom field is always resolved and available in the directive.

Of course it is trivial to implement such a check in this case, but imagine a far more complex model where you don't want to implement that check in each resolver and you want to make the permissions visible in your model definition.

@jlramosr
Copy link

Anything new about this? I need it! (without calling root resolvers previously)

@lastmjs
Copy link
Author

lastmjs commented Oct 15, 2019

I also still would love to have this. I have implemented a rather sophisticated and useful permissions model with schema directives, but without fragments I am requesting extra information that could have been returned in the selection set in the resolver that I overwrite. This does not scale! For each record I check permissions on, I have to do an extra request to the database, which is fine when requesting a few records, but with hundreds or thousands this grinds my server to a halt.

@yaacovCR
Copy link
Collaborator

A schema directive cannot modify the query -- it can only modify the schema.

Query modification can be done by the wrapping server (express-graphql, apollo-server), within the execution algorithm itself (graphql-js) or by creating a wrapping schema with a transform (graphql-tools).

Based on above, it seems what you want is a way to specify -- together -- what transforms you want the wrapping schema to apply and what schema directives you want the subschema to use.

That seems definitely doable, but probably not using the existing schema directive patterns.

@yaacovCR
Copy link
Collaborator

With the new functionality in PR #1463 (closing #1234), you can create a schemaTransform function that does two things:

(1) modifies the resolver within a given schema by calling mapSchema

(2) wraps the given schema with wrapSchema and a transform object that defines a { transformRequest } property adding the fields you need (you could also check out the perhaps strangely named existing AddReplacementSelectionSet, AddReplacementFragment or ReplaceFieldWithFragment transforms to see if they accomplish what you need in terms of adding fields -- I don't believe they actually replace the fields in question, that language is just taken from stitching where the field is not expected to exist on the subschema, but I think most if not all of them just add fields).

An example of this would be helpful, but hopefully the new docs in #1463 landing soon should get you started with what's possible.

@yaacovCR
Copy link
Collaborator

Note that I listed other options available to you to add fields that are probably more performant. By wrapping the schema, you will get two rounds of graphql-js execution. It would be better to have the server add the fields or if graphql-js had middleware that could easily do it.

@yaacovCR
Copy link
Collaborator

The new AddSelectionSets is a maybe better named transform that takes as arguments a source schema, a map of which selection sets to add based on the presence of types and/or enclosed fields, and an "initial type" for the selection set traversal which -- when dealing with a proxied request -- should usually be the expected return type of the proxying root field.

@yaacovCR
Copy link
Collaborator

yaacovCR commented Oct 6, 2020

Closing this stale issue. Feel free to reopen if there is anything we can be further help with...

@yaacovCR yaacovCR closed this as completed Oct 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants