Access context from Apollo GraphQL mutation field directive - graphql

I have an input type like this:
input PetGiraffe {
name: String #addUserLastName
}
Inside the directive, I need access to the request's context, so that I can add the user's last name to the giraffe's name. Here's the relevant part of what I've got so far:
const addUserLastNameDirective = {
typeDefs: gql`directive #addUserLastName on INPUT_FIELD_DEFINITION`,
transformer: (schema: GraphQLSchema, directiveName = 'addUserLastName') => {
return mapSchema(schema, {
[MapperKind.INPUT_OBJECT_FIELD]: (fieldConfig, fieldName, typeName, schema) => {
const directive = getDirective(schema, fieldConfig, directiveName)?.[0];
if (directive) {
// Need context in here because the user is in the context.
}
},
});
},
};
For queries, I understand you can override the fieldConfig.resolve method and get access to the context that way. But if I try that with this mutation, it throws: field has a resolve property, but Input Types cannot define resolvers.
The closest I could find was this from the graphql-tools docs, but that doesn't solve my problem of accessing the context.

Related

Omitting the field in response with GraphQL Apollo

I am using Apollo GraphQL server and directives.
Here is my simple schema. Notice the directive on the token field, User type.
const typeDefs = `
directive #allow(service: String) on FIELD_DEFINITION
type User {
email: String!
pass: String!
... other fields here
token: String #allow(service: "login")
}
type Mutation {
login(email: String!, pass: String!): User
}`;
I would like to return the token field only if the login has been called. Otherwise, I would like to return the User object without the token field, all I could find is throwing an Exception or returning the null in the "token" field.
class SkipDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field, details) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function (...args) {
// If called in context different from "login"
// Here I would like to just "delete" the "token" field
else {
const result = await resolve.apply(this, args);
return result;
}
};
}
}
Ideas?
If a field is requested, it should be returned with either a value matching the field's type or else null. To do otherwise would break the spec.
There is no way you can modify this behavior through a schema directive. A field definition directive can only change runtime behavior by modifying the field's resolver. However, by the time the resolver is called, the selection set has already been determined so it's too late to modify it. Returning null or throwing an error are pretty much the only two options.
You might be able to implement some kind of workaround through either the formatResponse option or a custom plugin. However, because this behavior would break the spec, there's no telling if it wouldn't cause issues with client libraries or other tools.

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