apollographql / federation

🌐  Build and scale a single data graph across multiple services with Apollo's federation gateway.
https://apollographql.com/docs/federation/
Other
667 stars 254 forks source link

Question: How to `inject` parameters for subgraph calls #1104

Open stengvac opened 3 years ago

stengvac commented 3 years ago

Hello,

I am not sure if this question belong here or to another repo so I will start here and please redirect me if I am not in correct place.

We have project called gateway-service, which accept graphql calls with JWT from front end. This project now use Apollo server with Apollo Federation. Micro service architecture - multiple backing services with subgraphs.

Our goal was to move JWT related security to this service and then calls to backend services. So we have implemented security directives, which are placed on root level fields and then verified with delegating resolvers. Those handles JWT token validation and role checking. This works nice.

Then we came with idea, then our backend services does not need to know about JWT at all. Possible solution was to use either headers to pass values or graphql variables - injected values from JWT to variables with predefined directives. We picked to use graphql varaibles, because graphql, exposed on BE could be reused like this for backend calls.

Little example: subgraph gql from our BE service

#this directive is marker for id injection from JWT
directive @injectId on ARGUMENT_DEFINITION
# this directive is security one - jwt has to be valid etc
directive @authenticated on FIELD_DEFINITION

type Query {
  getSomeData(customerId: ID! @injectId): Boolean @authenticated
} 

For our Front end we adjust those generated supergraphSdl a little and emove parameters with directive @injectId - filled from JWT. So exposed GQL looks like this

type Query {
  getSomeData: Boolean
} 

Now I am facing problem has to implement this injection.

So far we use LocalGraphQLDataSource with executable schema created with makeExecutableSchema (use apollo link to call BE for each BE service). Those are letfovers from schema stitching (I am strugling how to use RemoteDataSource with working security). Then this schema is wrapped with mapSchema with GraphQLFieldResolver<TSource, TContext, TArgs> which solve security checks. But I don't see any way how to modify graph variables, which will be send to our beck end services from those resolve functions.

Alternatively there is RemoteGraphQLDataSource but I don't see how to build those injected variables for BE with this source.

glasser commented 3 years ago

Moving this to the federation repo because that's where all federation stuff lives!

stengvac commented 2 years ago

In the end somehow working. Not sure, if it is clear solution.

Resolver function info object contains arguments, where we add those injected arguments.

return async function (source: any, args: any, context: ApolloCustomContext, info: GraphQLResolveInfo): Promise<any> {
            if (info.fieldNodes.length === 0) {
                throw new Error("Expected fieldNodes at least with size 1");
            }

           const injectedArgument: ArgumentNode = {
              kind: "Argument",
              name: {
                  kind: "Name",
                  //argument name is resolved during search for injectable argument
                  value: argumentName,
              },
              value: {
                  kind: "StringValue",
                  value: "injectedValueComesHere",
              }
          };

            const fieldArgs = info.fieldNodes[0].arguments ?? new Array<ArgumentNode>();
            // @ts-ignore - maybe better to make copy of info?
            info.fieldNodes[0].arguments = [...fieldArgs, injectedArgument];

            return resolver(source, args, context, info);
        };