Field variableValues not working on graphql library execution on node - graphql

I'm playing with the graphql library (https://github.com/graphql/graphql-js) on node but I'm having some hard time on passing variable attributes...
const variableValues = {
routing, // String
statuses, // Array
date // Input type described in the query
}
return graphql({
schema: schema,
source: query,
rootValue: resolvers,
variableValues: variableValues
})
Unfortunately the variableValues are not passed to the resolver (if I log the context from the resolver, it show me that the variableValues is an empty object).
Any suggestions?

Variable values are not passed to your context. Variables are used to substitute values inside an operation. So instead of using literal values like this:
query GetUser {
getUser(id: 42) {
name
}
}
we can write
query GetUser($userId: ID!) {
getUser(id: $userId) {
name
}
}
In this particular example, userId would be exposed to the resolver for getUser as the id argument. The arguments for a field are provided as a the second parameter to the resolver function, separate from the context (which is the third parameter passed to the resolver).
const resolvers = {
Query: {
getUser: (root, args, ctx) => {
console.log(args.id) // prints the value of $userId
...
},
},
}
Note that variables may be used as arguments to directives as well, in which case they will not be passed to the resolver as part of the argument map at all.

Related

Change backend service based on GraphQL Param

I have a GraphQL Schema as such:
BigParent (parentParam: Boolean) {
id
name
Parent {
id
name
Child (childParam: Boolean) {
id
name
}
}
}
How can I write resolvers such that I call different backend APIs based on whether the parentParam is true or the childParam is true? The first option is straight-forward. The second one needs to kind of reconstruct the Graph based on the values returned by the service data returned at the level of Child.
I'm not considering both the options as true, as I'll assign some priority so that the param at child level is not considered while the param at the parent level is passed.
You can get the arguments for any field selection in the query by traversing the GraphQL resolver info parameter.
Assuming your query looks like this:
query {
BigParent(parentParam: Boolean) {
id
name
Parent {
id
name
Child(childParam: Boolean) {
id
name
}
}
}
}
You should be able to do something like this:
function getChildParam(info) {
// TODO: Return 'childParam'
}
const resolvers = {
Query: {
async BigParent(parent, args, context, info) {
// 'args' contains the 'parentParam' argument
const parentParam = args.parentParam;
// Extract the 'childParam' argument from the info object
const childParam = getChildParam(info);
if (parentParam || childParam) {
return context.datasource1.getData();
}
return context.datasource2.getData();
},
},
};

How to map query fields to database table column? [duplicate]

I am using graphql-tools. After receiving a GraphQL query, I execute a search using ElasticSearch and return the data.
However, usually the requested query includes only a few of the possible fields, not all. I want to pass only the requested fields to ElasticSearch.
First, I need to get the requested fields.
I can already get the whole query as a string. For example, in the resolver,
const resolvers = {
Query: {
async user(p, args, context) {
//can print query as following
console.log(context.query)
}
.....
}
}
It prints as
query User { user(id:"111") { id name address } }
Is there any way to get the requested fields in a format like
{ id:"", name:"", address:"" }
In graphql-js resolvers expose a fourth argument called resolve info. This field contains more information about the field.
From the GraphQL docs GraphQLObjectType config parameter type definition:
// See below about resolver functions.
type GraphQLFieldResolveFn = (
source?: any,
args?: {[argName: string]: any},
context?: any,
info?: GraphQLResolveInfo
) => any
type GraphQLResolveInfo = {
fieldName: string,
fieldNodes: Array<Field>,
returnType: GraphQLOutputType,
parentType: GraphQLCompositeType,
schema: GraphQLSchema,
fragments: { [fragmentName: string]: FragmentDefinition },
rootValue: any,
operation: OperationDefinition,
variableValues: { [variableName: string]: any },
}
In the fieldNodes field you can search for your field and get the selectionSet for the particular field. From here it gets tricky since the selections can be normal field selections, fragments or inline fragments. You would have to merge all of them to know all fields that are selected on a field.
There is an info object passed as the 4th argument in the resolver. This argument contains the information you're looking for.
It can be helpful to use a library as graphql-fields to help you parse the graphql query data:
const graphqlFields = require('graphql-fields');
const resolvers = {
Query: {
async user(_, args, context, info) {
const topLevelFields = graphqlFields(info);
console.log(Object.keys(topLevelFields)); // ['id', 'name', 'address']
},
};
Similarly for graphql-java you may do the same by extending the field parameters with myGetUsersResolverMethod(... DataFetchingEnvironment env).
This DataFetchingEnvironment would be injected for you and you can traverse through this DataFetchingEnvironment object for any part of the graph/query.
This Object allows you to know more about what is being fetched and what arguments have been provided.
Example:
public List<User> getUsers(final UsersFilter filter, DataFetchingEnvironment env) {
DataFetchingFieldSelectionSet selectionSet = env.getSelectionSet();
selectionSet.getFields(); // <---List of selected fields
selectionSet.getArguments(); // <--- Similarly but MAP
...
}
In fact you may be alluding to look ahead data fetching. The above should give you enough insights into the fields requested and you can take it from there to tailor you downstream calls manually. But also you may look into a more efficient way to do this by using the data fetchers for Building efficient data fetchers by looking ahead

Explanation for different implementations of resolver function in graphql

I've been reading through the graphQL docs and found that they've explained the implementation of the graphql server in 2 ways: one using graphql-yoga which is a fully featured graphql server and another one is using graphql, express-graphql and express. In both cases, we pass the schema and resolver functions while creating the server instance.
But the implementation of resolver function differs. While using graphql-yoga, the resolver function is provided with 4 arguments which contains information about the parent object, arguments received, context, info. whereas in the other case (using graphql), the resolver function only gets the arguments object.
Why is that so ? If I want the info, context objects, how do I get it ?
Using graphql-yoga example: https://graphql.org/learn/execution/
Using graphql example: https://graphql.github.io/graphql-js/mutations-and-input-types/
// Code example using graphql
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`);
var root = {
rollDice({numDice, numSides}) {
return [1, 2];
},
addDice({numDice}) {
console.log("Adding something");
return "Added";
}
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
// Code example using graphql-yoga
let graphqlServer = require("graphql-yoga");
const typeDefs = `
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`;
const resolvers = {
Query: {
rollDice(parent, args, context, info) {
console.log(args.numDice);
console.log(args.numSides);
return [1, 2];
}
},
Mutation: {
addDice(parent, args, context, info) {
console.log(args.numDice);
return "Added";
}
}
};
const server = new graphqlServer.GraphQLServer({
typeDefs,
resolvers
});
server.start(() => {
console.log("server started on localhost:4000");
});
Difference between these 2 code snippets:
The resolver functions are present inside appropriate types (i.e. Query, Mutation) in one case. In the other case, they are present inside one root object. This means that I can have methods with same name in Query and Mutation in the first case, whereas in the second case that's not possible since they are keys of a single object and keys should be unique.
Why is this so ? Am I basically missing something ? How can the implementation details differ from one package to another ?
REAL TALK: the GraphQL.js docs are not that great. In my opinion, they never should have used examples with buildSchema in the first place because it understandably leads to this kind of confusion.
GraphQL.js (i.e. the graphql package) is the JavaScript implementation of GraphQL. Building a schema in GraphQL.js is done programmatically, by constructing an instance of the GraphQLSchema class:
const userType = new GraphQLObjectType({
name: 'User',
fields: {
id: {
type: GraphQLID,
},
email: {
type: GraphQLString,
},
},
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
resolve: () => ({ id: 1, email: 'john.doe#example.com' }),
},
},
});
const schema = new GraphQLSchema({
query: queryType,
})
If we print this schema in Schema Definition Language (SDL), it looks like this:
type Query {
user: User
}
type User {
id: ID
email: String
}
Working with SDL is much easier than having to write out all that code. However, GraphQL.js does not provide a way to build a fully-featured schema from SDL. It does provide a buildSchema function, but this utility constructs a schema without any resolvers (and a number of other features like union/interface type resolution).
The graphql-tools package provides a makeExecutableSchema function that lets you build a schema from SDL and a resolver map object. This is what's used under the hood by apollo-server and graphql-yoga. makeExecutableSchema constructs a schema from SDL using buildSchema and then mutates the resulting object, adding the resolvers in after the fact.
In GraphQL.js, the resolve function (or resolver) for a field takes four parameters -- the parent value, the field's arguments, the context and a GraphQLResolveInfo object. If we're creating a GraphQLObjectType like userType in the above example, this is the optional function we can provide for each of the fields in our object. This is the same function you define when you construct a resolver map to use with graphql-yoga. This is the only implementation of a field resolver.
So what's the deal with buildSchema??
The examples in the docs take advantage of GraphQL's default field resolver:
export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
source,
args,
contextValue,
info,
) {
if (typeof source === 'object' || typeof source === 'function') {
const property = source[info.fieldName];
if (typeof property === 'function') {
return source[info.fieldName](args, contextValue, info);
}
return property;
}
};
As you can see, the default resolution logic looks for a property with the same name as the field on the source (parent) value. In our example above, the user resolver returns {id: 1, email: 'john.doe#example.com'} -- this is the value the field resolves to. The field is of the type User. We do not have a resolver defined for our id field, so the default resolver does its thing. The id field resolves to 1 because that's the value of the property named id on the parent object the resolver receives.
However, the parent value can also be a function instead of an object. If it's a function, it gets called first and then the return value is used. What does the function get called with? Well, it can't pass it a parent value (because of infinite recursion), but it can pass it the remaining three parameters (args, context and info). So that's what it does.
Now for the magic trick 🎩🐇
In our example, I can omit the resolver for the user field and pass a function to the root value instead.
const root = {
user: () => ({id: 1, email: 'john.doe#example.com'})
}
The root object is just an optional object that's passed down as the parent value to resolvers at the root level (like your Query or Mutation types). Otherwise, those resolvers would not have a parent value.
Query is an operational root type -- it serves as an "entry point" to the rest of your schema. Any fields on the Query type will be passed the root object as the parent value. If I omit a resolver for the user field, the default resolver will 1) examine the parent object for a property with the same name, 2) find a property and determine that it's a function, 3) call the function, 4) resolve the field to the return value of the function.
TADA!
However, because the function is called by the default resolver, and is not used as a resolver itself, it will only receive the three aforementioned parameters, instead of 4.
This is a neat way to work around not being able to actually provide custom resolvers for a schema, but it's very limited. It only works for root types, so we can't similarly provide fake resolvers for User fields or other types. We can't use interfaces or unions in our schema because we can't provide resolveType functions. And so on...
Hopefully that provides some clarity. And hopefully we can get the docs updated in the near future to avoid all this confusion in the first place.

Apollo GraphQL: Resolver not called on mutation subfield

I am trying to return a Query type from a mutation, which I could make work in some cases but not the way I want it. The problem is not particularly related to the Query type being used, as I found the same behaviour using other types than Query.
You can run and modify this code on https://codesandbox.io/s/1z8kjy8m93
Server
const { ApolloServer, gql } = require("apollo-server");
const typeDefs = gql`
type Query {
hello(msg: String): String
}
type Mutation {
someMutation(someArg: String): MutationResponse
}
type MutationResponse {
query: Query
status: String
}
`;
const resolvers = {
Query: {
hello: (root, args, context) => {
console.log("hello: args = ", args);
return `${args.msg}, world !`;
}
},
Mutation: {
someMutation: (root, args, context) => {
console.log("someMutation: args = ", args);
return { status: `Mute Mute: ${args.someArg}` };
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Mutation
mutation mutateMe($mutationArg: String = "YoloMute !", $helloMsg: String = "Yolhello") {
someMutation(someArg: $mutationArg) {
status
query {
hello(msg: $helloMsg)
}
}
}
Response
{
"data": {
"someMutation": {
"status": "Mute Mute: YoloMute !",
"query": null
}
}
}
I don't understand why the hello resolver is not called and the query field is null.
The status field is duly filled by the someMutation resolver, but as the query field is not resolved there I would expect GraphQL to call an existing resolver for this field, which exists and should be called for the Query type.
I found other ways that technically work but are not satisfying:
Directly returning the Query type works:
https://codesandbox.io/s/ovr9zpwr7q
Duplicating lines in the schema
works: https://codesandbox.io/s/l4yrqzmq6l
This issue isn't really specific to the Query type, but rather deals with how you've set up your resolvers.
The status field is duly filled by the someMutation resolver, but as the query field is not resolved there I would expect GraphQL to call an existing resolver for this field, which exists and should be called for the Query type.
There is no resolver for the entire Query type, or any other type. Resolvers exist only for individual fields of a particular type. When a resolver isn't defined for a field, GraphQL will default to looking for a property on the parent object with the same name as the field, and will return the value of that property.
Let's walk through your document. The root-level field is:
someMutation(someArg: $mutationArg)
The parent value is the root value for all root-level mutations. Unless you're using a custom root value, this will typically be an empty object. If you didn't define a resolver for the someMutation field of the Mutation type, GraphQL would look for a property called someMutation in your root value and return that (i.e. undefined, which would be coerced to null in the response). We do have a resolver, though, and it returns:
{
status: `Mute Mute: ${args.someArg}`,
}
Now, let's resolve the status field. Our parent object is the result returned by the parent field's resolver. In this case, the object above. We have no resolver for status on MutationResponse, so GraphQL looks for a status property on the parent -- it finds one and uses that. status has a Scalar type, so whatever value is returned by the resolver will be coerced into the appropriate scalar value.
What about the query field? Again, we have no resolver for a query field on the MutationResponse. However, we also don't have a property called query on the parent object. So, all GraphQL can do is return null for that field.
Even though the return type for query is an ObjectType, because it resolves to null, any resolvers for fields on that ObjectType will not be fired. Returning null means the object doesn't exist, so we don't need to bother resolving any fields on it. Imagine if a field returned a User object. If it returned null, there would be no need to resolve the user's name, for example.
So... how do we get around this? There's two ways:
Either add a property for query to the object returned by someMutation's resolver, like this:
{
status: `Mute Mute: ${args.someArg}`,
query: {},
}
Or, add a resolver for the field:
MutationResponse: {
query: () => {},
},
Either way will ensure that the query field will resolve to a non-null value (in this case, just an empty object). Because the value resolved is not null and the return type is an ObjectType (in this case Query), now the resolvers for that type's fields will be triggered and hello will resolve as expected.

How to get requested fields inside GraphQL resolver?

I am using graphql-tools. After receiving a GraphQL query, I execute a search using ElasticSearch and return the data.
However, usually the requested query includes only a few of the possible fields, not all. I want to pass only the requested fields to ElasticSearch.
First, I need to get the requested fields.
I can already get the whole query as a string. For example, in the resolver,
const resolvers = {
Query: {
async user(p, args, context) {
//can print query as following
console.log(context.query)
}
.....
}
}
It prints as
query User { user(id:"111") { id name address } }
Is there any way to get the requested fields in a format like
{ id:"", name:"", address:"" }
In graphql-js resolvers expose a fourth argument called resolve info. This field contains more information about the field.
From the GraphQL docs GraphQLObjectType config parameter type definition:
// See below about resolver functions.
type GraphQLFieldResolveFn = (
source?: any,
args?: {[argName: string]: any},
context?: any,
info?: GraphQLResolveInfo
) => any
type GraphQLResolveInfo = {
fieldName: string,
fieldNodes: Array<Field>,
returnType: GraphQLOutputType,
parentType: GraphQLCompositeType,
schema: GraphQLSchema,
fragments: { [fragmentName: string]: FragmentDefinition },
rootValue: any,
operation: OperationDefinition,
variableValues: { [variableName: string]: any },
}
In the fieldNodes field you can search for your field and get the selectionSet for the particular field. From here it gets tricky since the selections can be normal field selections, fragments or inline fragments. You would have to merge all of them to know all fields that are selected on a field.
There is an info object passed as the 4th argument in the resolver. This argument contains the information you're looking for.
It can be helpful to use a library as graphql-fields to help you parse the graphql query data:
const graphqlFields = require('graphql-fields');
const resolvers = {
Query: {
async user(_, args, context, info) {
const topLevelFields = graphqlFields(info);
console.log(Object.keys(topLevelFields)); // ['id', 'name', 'address']
},
};
Similarly for graphql-java you may do the same by extending the field parameters with myGetUsersResolverMethod(... DataFetchingEnvironment env).
This DataFetchingEnvironment would be injected for you and you can traverse through this DataFetchingEnvironment object for any part of the graph/query.
This Object allows you to know more about what is being fetched and what arguments have been provided.
Example:
public List<User> getUsers(final UsersFilter filter, DataFetchingEnvironment env) {
DataFetchingFieldSelectionSet selectionSet = env.getSelectionSet();
selectionSet.getFields(); // <---List of selected fields
selectionSet.getArguments(); // <--- Similarly but MAP
...
}
In fact you may be alluding to look ahead data fetching. The above should give you enough insights into the fields requested and you can take it from there to tailor you downstream calls manually. But also you may look into a more efficient way to do this by using the data fetchers for Building efficient data fetchers by looking ahead

Resources