GraphQL query and check the returned data - graphql

I would like to have a query containing a check, for example when querying users, only users that have a non-null email are wanted. How could I add such check? Thanks!
users {
id,
username,
email
}

GraphQL fields can have arguments.
So you could pass an argument to your users field, named onlyNonNullEmail: boolean:
type Query {
users(onlyNonNullEmail: Boolean) {
...
}
}
Then, inside your resolve function:
users: (_, args, context, ast) => {
const { onlyNonNullEmail } = args;
// If onlyNonNullEmail is true, return only users with a non-null email
// Else proceed as usual
}

Related

Wrong type return in Resolver does not trigger error

I am building a sever with Graphql. I define a single query which should return a User type. However, in my resolver I am actually returning a string type: "Hello!".
The query executes in the playground without any problem.
My question: the return statement of the resolver expects a User type, instead returns a string. Why is it not failing?
TypeDef
type User {
id: String
email: String
name: String
}
type Query {
printUser: User
}
Resolver
const resolvers = {
Query: {
printUser: async (parent, args, context, info) => {
return 'Hello!';
}
}

Nested field resolvers in GraphQL

The goal is to use NestJS to implement a GraphQL schema using the code-first approach.
Let's say I have a Pet type in my GraphQL schema with two fields, name and age. If those two pieces of information come from different sources of truth (and I don't always want to fetch both), I could implement a PetResolver class with resolvers for each field:
#Resolver(() => Pet)
export class PetResolver {
#Query(() => Pet)
pet(): Pet {
return {};
}
#ResolveField()
name(): Promise<string> {
return Promise.resolve('Spot');
}
#ResolveField(() => Int)
age(): Promise<number> {
return Promise.resolve(2);
}
}
which could be used like this:
query GetPet {
pet {
name
}
}
This works and would ensure that the value of each field is only fetched when requested, but what if I wanted to have a pet field on my User type that I could query like this:
query GetUserWithPet {
currentUser {
email
pet {
name
}
}
}
Applying the same principle, I could create a UserResolver class like this:
#Resolver(() => User)
export class UserResolver {
#Query(() => User)
#UseGuards(AuthCognitoGuard)
currentUser(#CurrentUser() user: IdTokenPayload): User {
return {
id: user.sub,
email: user.email,
};
}
#ResolveField()
pet(#Parent() user: User): Promise<Pet> {
return petService.getPetForUserId(user.id);
}
}
but then the PetService implementation would have to be aware of which fields were requested if it only wanted to fetch relevant data.
A) Is there a way to use PetResolver within UserResolver to make use of the individual field resolution logic?
B) If not, what is the best way to determine which fields were requested in the query using NestJS code-first conventions?
C) Is this the "wrong" way to think about GraphQL queries? Do best practices dictate that I keep the separate resolver and use a query like this:
query GetUserWithPet {
currentUser {
email
}
pet {
name
}
}
User should contain some petIds [array] value (internal, DB stored field/column) ...
... making possible to resolve pets: [Pet] prop/relation - list of Pet ...
... like starshipIDs explained in https://graphql.org/learn/execution/
Notice: pets service is asked about records using pet ids.
... but of course pet can contain some ownerId (only or explicitely visible, DB stored field/column) making possible to resolve owner: User prop [reverse] relation - this way you can:
query PetWithOwner {
pet (id: "someID") {
id
name
owner {
id
email
# more pets?
pets {
id
name
# you can loop it ;)
owner {
id
email
pet.owner field resolver can return only { id: ownerId } object (partial response) ... server will try to resolve 'missing' (required by query) email prop using User (owner is User type) type resolver, passing id as an arg (check/console.log parent and args resolver args). You don't have to do it [the same] 'manually' inside pet.owner field resolver.
Query required fields ...
... [selection set] can be read from info object - 4th resolver arg - read docs/tutorial for details

Field variableValues not working on graphql library execution on node

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.

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

Resources