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

allow custom directives #53

Open
ngmariusz opened this issue Feb 22, 2019 · 28 comments
Open

allow custom directives #53

ngmariusz opened this issue Feb 22, 2019 · 28 comments
Labels
needs/upstream An upstream component needs to be updated first scope/gql-spec type/feat Add a new capability or enhance an existing one

Comments

@ngmariusz
Copy link

if i understand correctly there is only @deprecated possible so far? it there way to add custom ones?
tools out there usually depend on custom directives then i could integrate nexus into toolchain

@tgriesser
Copy link
Member

We can add the ability to define a directive for runtime use, but (as far as I can tell) adding them for use annotating the SDL would be more difficult, due to lack of support in graphql-js:

Can you talk more about the type of directives you are looking to add and how they would be used?

@tgriesser tgriesser added the question Further information is requested label Feb 22, 2019
@ngmariusz
Copy link
Author

ngmariusz commented Feb 25, 2019

Sure,

So Basically my question was motivated with context the of trying to use Nexus in already existing Project or pipeline (which is schema first)
Let’s say i have a set of custom directives syntax that i use to annotate schema and then parse with my cli tools to generate resolvers, db and so on
As Nexus actually generates schema i’ll put it on the beginning of tool chain try to achieve same result as i would write by hand in my .graphql files.
So finally i would just like to be able to achive in exp this

type User @entity {
  id: ID! @id
  email: String! @column @map(path: "login.email") @depreciated(reason: 'lorem')
}

with sth like

const User = objectType({
 annotate:'@entity',
 name: 'User',
 definition(t) {
   t.string('email', { annotate:'@column @map(path: "login.email")', deprecation: 'lorem', nullable: true });
   t.id('id', { annotate:'@column' })
 }
});

i understand that will be implementing workaround (until it's figured out in graphql-js) perhaps by repacing
printSchema in generateSchemaFile with graphql/graphql-js#869 (comment)

without this feature switching to Nexus seems like I have to leave behind everything that already works

@tgriesser
Copy link
Member

I think this should be possible to add, going to dig into what it'll take to do this properly and keep you posted.

@tgriesser tgriesser added type/feat Add a new capability or enhance an existing one and removed question Further information is requested labels Mar 4, 2019
@jregistr
Copy link

jregistr commented Apr 3, 2019

👋 Hellos! I'm also interested in this. Perhaps all we'd need for implementing this is the ability to specify directives for type and field creation?
At a glance, looks like one can use apollo's graph-tools to make the schema while providing directives.

@tgriesser
Copy link
Member

👋

Perhaps all we'd need for implementing this is the ability to specify directives for type and field creation? At a glance, looks like one can use apollo's graph-tools to make the schema while providing directives.

Yes - you can add these in graphql-tools, but It's not quite as simple to do programmatically because they needed to be added to the AST for the schema, which is possible but takes a bit more work.

I've started work on it in this branch, it's something I plan on getting to but it still needs quite a bit of work to get the type generation, etc working properly.

@tgriesser
Copy link
Member

Actually this looks like it might also be simple to do programmatically extendSchema, will take a look into that approach.

@trufa
Copy link

trufa commented May 13, 2019

Any news on this?

@Mika83AC
Copy link

I'd also urgently need custom directives in nexus for applying authorization on query and field levels without highly repetetiv code in all resolvers.

@Schubidu
Copy link

Any news about directives. I want to use Apollo Federation and I need to use directives on nexus.

@Schubidu
Copy link

Oh I just found a related issue #148

@jhalborg
Copy link

Also relevant for caching with Apollo Server. We are trying to map out what it would take for us to migrate from graphql-js to nexus, and this feature is a blocker as we are quite reliant on directives on our platform

@jhalborg
Copy link

jhalborg commented Nov 4, 2019

Any movement on this @tgriesser , active or planned?

@tgriesser
Copy link
Member

Any movement on this @tgriesser , active or planned?

There is movement planned, but this is blocked on longer term changes from graphql-js end. I have discussed in depth with @IvanGoncharov and I believe the current thinking is that the extensions property (added in the 14.5.0 release) on all core type objects will eventually have a way to encode metadata into both the introspection as well as the emit of the SDL.

You can read up on the related issue graphql/graphql-js#1343, but the gist of it is that the SDL was never intended to be used as the means to construct the schema, and the @deprecated directive is only present in the SDL because it can also be represented in the introspection query:

fragment FullType on __Type {
  # ...
  fields(includeDeprecated: true) {
    name
    # ...
    isDeprecated
    deprecationReason
  }
  enumValues(includeDeprecated: true) {
    name
    # ...
    isDeprecated
    deprecationReason
  }
  # ...
}

The fact that handwritten directives exist in the AST when writing-SDL first is only a result of that fact that it is valid syntax, but it's not necessarily meant to be utilized in this way.

So I'm planning on revisiting in the future when this is supported in the way that is intended, rather than trying to hack around this limitation.

@jhalborg
Copy link

jhalborg commented Nov 5, 2019

@tgriesser Allright, thanks, that makes sense.

So for people looking to use a code-first approach with Apollo Federation, the short answer seems to be: "You can't" - at least yet, because Lee Byron and the GraphQL team does not believe that using directives in such a manner is the "intended" way of the GraphQL IDL?

@tgriesser
Copy link
Member

Yeah that's more or less it. I think I had seen mention there was or will be an approach of federating via code first, but I'm not positive.

I'd also caution against jumping right to federation just because it's the new tech out there. Facebook, Shopify, Github, Twitter all examples of companies/codebases using GraphQL which are code-first and not federated at the GraphQL layer.

If you're moving to federation because you truly have multiple independent GraphQL services you control and really want to stitch together at the GraphQL layer (say, one in Ruby, one in Scala, one in Elixir, one in JS), then yes - federation seems like a possible solution.

Instead, if you're using federation to try and compartmentalize pieces of your app, I'd advise seeing how far code-first can get you. You can still come up with a set of conventions for where to place code so different teams can work on it and expose their pieces of the graph in ways that don't step on others' toes, particularly with helpers like extendType, mutationField, and queryField.

In my view, the ideal GraphQL layer hides the implementation details of any microservices/data stores at a lower level. As soon as you federate, you will run into a host of other problems you'll need to sort out.

@marcotuna
Copy link

marcotuna commented Mar 24, 2020

I know that my comment will add nothing but currently we are at the end of March, is there any updates? Any workaround to add custom directives to use for example apollo-server cache and rateLimit library?

@BondDimy
Copy link

BondDimy commented Apr 9, 2020

Hello, I've also faced with this problem, is there any updates ? I think now we are looking for any solutions. Thanks

@maxwellsmart84
Copy link

bumping this as I just want to add a "@deprecated" flag to some queries/mutations

@Mika83AC
Copy link

@maxwellsmart84 you don't need directives for that. You can just set the "decprecation" property on the field/object and it will be deprecated in schema.

@yaacovCR
Copy link

yaacovCR commented Dec 22, 2020

see #148 (comment)

i think there are two issues, allowing uses of custom directives within the schema and allowing definition of custom directives that could be used within the schema or the query...

the former is debatable, and graphql-tools has joined graphql-compose/gatsby in saying that these "directive" uses should be alternatively read from the extensions...

but the latter seems definitely like it should be supported

maybe it is supported? i am day #1 on using nexus so forgive me if I am incorrect.

I used a workaround with graphql-js extendSchema to add the definitions via SDL, but that seems less than ideal -- in contrast, my code-first stitching example with graphql-js lets me add the built directives.

@bionicles
Copy link

bionicles commented Mar 8, 2021

just want to argue this feature would be powerful for authorization, which ought to happen at the GraphQL layer, since it handles the data schema.

ideas of trust and ideas of data are forcibly linked because you can't make trust decisions without data and you can't just serve data without trust in many situations (finance, healthcare etc),

so some standard way to meta-program the schema would prevent us from doing a lot of boilerplate repetition

@Mark-Panda
Copy link

Is there any latest news?

1 similar comment
@Mark-Panda
Copy link

Is there any latest news?

@nicolastoulemont
Copy link

nicolastoulemont commented Mar 30, 2021

just want to argue this feature would be powerful for authorization, which ought to happen at the GraphQL layer, since it handles the data schema.

ideas of trust and ideas of data are forcibly linked because you can't make trust decisions without data and you can't just serve data without trust in many situations (finance, healthcare etc),

so some standard way to meta-program the schema would prevent us from doing a lot of boilerplate repetition

@bionicles If your are looking to abstract your authorization in an easily consumable service, I suggest that you have a look at the field authorize plugin, it may be what you are looking for.

If it is not, you can always write your own custom plugins based on your need. For authorization, I would suggest writing a plugin on the onCreateFieldResolver operation. Here are the docs it's a fairly simple task and you can always have look at the default plugins source code here for inspiration :).

Good luck

@hinsxd
Copy link

hinsxd commented Apr 3, 2021

In our case, we'd like to use custom directive to expose more information to the frontend, so as to generate some fields dynamically. Is custom directive the right way to extend a GraphQL* type, or is there any better way to expose extra metadata to the frontend?

@R9295
Copy link

R9295 commented May 11, 2021

In my use-case, I would like to replace the value of the field with null if the user is not authorized. Any updates on this feature?

@Sytten
Copy link
Collaborator

Sytten commented May 21, 2021

I just did a deep dive into the subject and here is my proposition @tgriesser @jasonkuhrt

Recap of facts

  • Directives are an SDL artifact, this means they are parsed from the SDL and stored in the ASTNode but they are not on the schema objects themselves. This was a decision by people maintaining graphql-js because directives were not really meant to be used that way? The spec is pretty sh*t on that subject and honestly I don't agree with the decision of the graphql-js maintainers on that (the justification being that if you use code-first you can just place your metadata in code and wrap the resolver).
  • The previous discussion was in 2018, now in the meantime people pushed to get a standardized place to put extra data. This is what lead to extensions on the schema objects. The problem is that it is just a dictionary without structure and there was no move to explicitly put directives there. Over time it became the standard though and nexus can easily support it since it allows for custom extensions.
  • The problem is that some 3rd party extension (apollo being the primary offender) just checks for directives in the ASTNode and don't allow for configuration via extensions. Now this is kinda annoying for people trying to build a federation (whatever it is a good idea vs using stitching is irrelevant for the current discussion). The federation tooling actually has its own internal printer to print the directives back to SDL and it has also trouble with graphql-middleware because it adds some props on the schema objects which are discarded (see Federated schemas are not compatible with graphql-middleware apollographql/federation#737).
  • Every other problem (like @R9295 just to pick one) can easily be solved by a nexus plugin and don't require directives at all.

Proposition
We could take a similar approach to Type-GraphQL directives. I don't consider user adding directives directly important since the primary way of adding custom logic should be plugins, but we definitely want plugins to be able to add directives. Internally we should just create the ASTNode to host them. I prefer this method to telling the user to use graphql-tools SchemaDirectiveVisitor, but there might be a technical reason for that that I am missing.

References

@bionicles
Copy link

bionicles commented Jun 6, 2021

I just want to take functions and apply them to fields or types, and read clear docs on arguments / desired outputs. IDK what it's called but I do care about having as little boilerplate as possible

If plugins are an alternative, then how does the code look? Are the docs enough for a newbie to read them and attach their custom function to multiple fields or types without questions? If so, we're done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs/upstream An upstream component needs to be updated first scope/gql-spec type/feat Add a new capability or enhance an existing one
Projects
None yet
Development

No branches or pull requests