Open pepijno opened 5 years ago
Huh? Introspection (according to the specification) does not expose any server-side directives (directives on any schema element definition, commonly supplied via SDL). Only client-side directives are visible (directives that a client can send as a part of a query). If this somehow breaks Apollo, Apollo is breaking the GraphQL spec, and the issue should be opened there.
The 3 directives SPQR adds are strictly server-side, and the client has no business sending them, nor knowing about them. You could add directives of the same name to the client-visible list (by using generator.withAdditionalDirectives(...)
), but it is semantically incorrect to do so... What would a query such as:
{
books {
title @_mappedType(type = ???)
}
}
even mean?
*Just to clarify, my terminology here is my own, but by server-side directives I mean the ones allowed only on any of: SCHEMA
, SCALAR
, OBJECT
, FIELD_DEFINITION
, ARGUMENT_DEFINITION
, INTERFACE
, UNION
, ENUM
, ENUM_VALUE
, INPUT_OBJECT
, INPUT_FIELD_DEFINITION
.
By client-side, I mean the directives allowed on: QUERY
, MUTATION
, SUBSCRIPTION
, FIELD
, FRAGMENT_DEFINITION
, FRAGMENT_SPREAD
, INLINE_FRAGMENT
.
Btw, there are talks in the GraphQL workgroup about introducing a way to make some server-side directives visible via introspection, but it hasn't yet gone anywhere concrete. There's a related issue in graphql-java as well.
That said, I'm still curious to understand what goes wrong. Can you provide some more info/examples? If there's a sensible workaround, I'd be open to that.
Ah, then I think I know the problem. I try to use Apollo federation and in order to do that I use the federation-jvm repostiroty (https://github.com/apollographql/federation-jvm). The Apollo federation service gets the different schemas by using a query which fetches the SDL. Federation-jvm creates this SDL including all directives which means that ALL directives are exposed to the federation service. I created a simple application which shows this problem: https://github.com/pepijno/graphql-apollo-directives.
Unfortunately I want some directives to be exposed to the federation service so it is not possible to just simply create the SDL without directives. Is there any easy way to add the server-directives to the client-visible list? The Directives
class in spqr
generates a directive for each AnnotatedType
.
I stumbled on the internals by using graphql.schema.idl.SchemaPrinter
to see what was generated and wondered what these directives were and what they would mean from the clients perspective. I wonder if these is some way to exclude them from the schema before printing it.
I'd also like the ability to printout to file a schema that a client can use to call the server. Currently, it is full of these directives. As mentioned in #312
I would like to ask about federation support. I prefer spqr approach to generate schema out of entities, but I miss a good way how to connect many microservices with GraphQL endpoints with single gateway. Schema federation seems the easiest approach, but it needs some special setup not available in spqr.
@pepijno @TuomasKiviaho @iamlothian @Marx2 I'm very much interested in doing what's needed to make Apollo Federation work, but I unfortunately do not understand at all what would that entail. I went through Apollo docs and they make no sense to me whatsoever. They seem to require SDL and not introspection, and expose server-internal directives to the client etc, which all sounds bonkers.
Would one of you who have a real example be interested in helping me understand what's needed in SPQR?
As for hiding the SPQR-internal directives from the printout, there's nothing I can do about it. SchemaPrinter
is coming from graphql-java and has no filtering support. I suppose if I make a PR to add the feature , they'd be willing to accept it, but it's ultimately not up to me...
As I understand, adding https://github.com/apollographql/federation-jvm to spqr project and making a configuration class should do the magic. I think it could be the best to make such an example in spqr source tree. Do the example, download and start Apollo federation app, and it should work. Maybe somebody had such an example. If not, I can try build one, but as I'm new in graphql world, it'll take some time
Suppose you already have a graphql schema. What the federation-jvm does is it adds two queries to the root, _service
and _entities
:
type Query {
...
_service: _Service
_entities(representation: [_Any!]!): [_Entity]!
}
scalar _Any
union _Entity = ...
type _Service {
sdl: String!
}
What gives all the trouble however is the sdl
field in the _Service
type. As the name suggests, sdl
returns the sdl of your original graphql schema. The federation server which stitches all the different uses this sdl
field to get the sdl from all different services, reads it and then combines them into one. The federation-jvm project uses the following options when printing the schema
SchemaPrinter.Options.defaultOptions()
.includeScalarTypes(true)
.includeExtendedScalarTypes(true)
.includeSchemaDefintion(true)
.includeDirectives(true);
which also prints all the directives @_mappedType
, @_mappedInputField
and @_mappedOperation
. Unfortunately, you also use directives such as @external
to tell the federation server which types can be referenced so the sdl needs to include the directives. Using the above printer options the definitions of @_mappedType
, @_mappedInputField
and @_mappedOperation
are not included in the schema and this is what makes the federation server fail when it parses the sdl.
One possible solution might be to add the definitions of the missing directives to the schema. I've tried to do it manually but it seemed like an awfull lot of work. Another one is to indeed change the SchemaPrinter
to not print the internal directives in some way or another. Or change the federation-jvm project to create the sdl in a different way?
Does this help @kaqqao ?
Seems like https://github.com/graphql-java/graphql-java/pull/1721 is included in graphql-java v14, only thing needed now is apollographql/federation-jvm to upgrade to v14 and expose an API that allows passing options to the schema transformer which would allow you to exclude _mappedType
, _mappedInputField
and _mappedOperation
from the SDL?
I have opened a Pull Request in federation-jvm which will allow excluding the _mappedType, _mappedInputField and _mappedOperation
directives from schema that gets printed by FederationSdlPrinter in federation-jvm library.
I have tested the changes on my local and finally able to run spqr with federation-jvm.
Here is the link for the Pull Request for reference - https://github.com/apollographql/federation-jvm/pull/80
@kaqqao it seems finally we will be able to run Apollo Federation based micro-services built with spqr.
@williamboman
Seems like graphql-java/graphql-java#1721 is included in graphql-java v14, only thing needed now is apollographql/federation-jvm to upgrade to v14 and expose an API that allows passing options to the schema transformer which would allow you to exclude
_mappedType
,_mappedInputField
and_mappedOperation
from the SDL?
I tried to add a feature in federation-jvm to exclude those directives from being printed in SDL. See this PR https://github.com/apollographql/federation-jvm/pull/80
However, the PR isn't accepted by federation-jvm (check their responses on PR) and instead they have suggested to put the directive definitions in schema by ourselves. I have tried that and it works. I am able to use SPQR with federation-jvm and apollo gateway in front.
Is this going to break anything with SPQR? Though i have tried queries implementing above approach and they seem to work fine.
As I understand, adding https://github.com/apollographql/federation-jvm to spqr project and making a configuration class should do the magic. I think it could be the best to make such an example in spqr source tree. Do the example, download and start Apollo federation app, and it should work. Maybe somebody had such an example. If not, I can try build one, but as I'm new in graphql world, it'll take some time
@Marx2 Were you able to build one example? I've been searching for some docs/examples, but couldn't find any. Any help is appreciated.
Here is a working example of how to use SPQR with Apollo Federation https://github.com/ankit-joinwal/graphqpl-spqr-federation-poc
Here is a working example of how to use SPQR with Apollo Federation https://github.com/ankit-joinwal/graphqpl-spqr-federation-poc
Thanks Ankit!
Thanks @ankit-joinwal, that's incredibly helpful!
It's also non-trivial to figure out; I feel very lucky to have stumbled upon that comment. I'd vote this example be promoted to the samples project and/or mentioned in the docs.
@ankit-joinwal - Thanks for the example above. Works fine for the most part except for input type on mutations.
For example:
@GraphQLMutation
public UserResponse createUser(@GraphQLArgument(name = "userRequest",
description = "request to create a user") UserRequest request) {
return new UserResponse();
}
Produces the following on the federated schema:
"Mutation root"
type Mutation {
createUser(
"request to create a user"
userRequest: UserRequestInput
): UserResponse @_mappedOperation(operation : "__internal__")
}
input UserRequestInput @_mappedType(type : "__internal__") {
id: Long! @_mappedInputField(inputField : "__internal__")
request: RequestInput @_mappedInputField(inputField : "__internal__")
users: [UserInput] @_mappedInputField(inputField : "__internal__")
}
Composing a graph on apollo complains about:
Directive "@_mappedType" may not be used on INPUT_OBJECT
Any thoughts?
... Composing a graph on apollo complains about:
Directive "@_mappedType" may not be used on INPUT_OBJECT
Any thoughts?
@krisiye just add another validLocation like that:
GraphQLDirective mappedTypeDirective = GraphQLDirective.newDirective()
.name("_mappedType")
.description("")
.validLocations(Introspection.DirectiveLocation.INPUT_OBJECT, Introspection.DirectiveLocation.OBJECT)
.argument(GraphQLArgument.newArgument()
.name("type")
.description("")
.type(unrepresentableScalar)
.build()
)
.build();
@adil-rakhimbekov - Perfect. That was what I was looking for. Worked great! Thank you for your help on this one!
SPQR adds the directives _mappedType, _mappedInputField and _mappedOperation to types, fields, queries and more. When doing introspection on the schema (for example with the query
graphql.introspection.IntrospectionQuery.INTROSPECTION_QUERY
) the three directives are not listed as part of the schema. Some query validations in apollo throw errors due to the fact that the definition of the directives is not in the schema.Is there a way to get the definition of the directives in the schema?