GraphQL resolvers only resolving the first type - graphql

so im working on a project of mine and i seem to hit a dead end with my abilities.
I am working on a GraphQL backend that is supposed to fetch some data from a MySQL database. I already got the resolvers working so i can fetch all users etc. but i am not able to fetch nested types. For example:
query {
ways {
id
distance
duration
purpose
User {
id
dob
}
}
}
This only returns all the ways from my database but the User returns null
schema.ts
export const typeDefs= gql`
schema {
query: Query
}
type Query {
ways: [Way]
users: [User]
getUser(id: String!): User
getWay(id: String!):Way
}
type Way{
id:String
distance:Float
duration:Float
stages:[Stage]
purpose:String
User:User
}
type User{
id:String
firstname:String
lastname:String
sex:String
dob:String
annualTicket:Boolean
Ways:[Way]
}
resolver.ts
export const resolvers = {
Query: {
ways: async(parent, args, context, info) => {
console.log("ways")
const answer=await getAllWays();
return answer;
},
users: async(parent, args, context, info) => {
console.log("users")
const answer=await getAllUser();
return answer;
},
getUser: async(parent, args, context, info) =>{
console.log("getUser")
const answer=await getUserPerID(args.id);
return answer;
},
getWay:async(parent, args, context, info) =>{
console.log("getWay")
const answer=await getWayPerID(args.id);
return answer;
}
},
User:async(parent, args, context, info) =>{
console.log("User: id="+args.id)
ways: User => getWaysPerUserID(args.id)
},
Way:async(parent, args, context, info) => {
console.log("Way: id="+parent.userID)
user: Ways => getUserPerID(parent.userID)
}
I would expect the outcome to include the user and its data too, using the above mentioned query.
Any advice is much appreciated.

Only fields, not types, can have resolvers. Each key in your resolvers object should map to another object, not a function. So instead of
Way:async(parent, args, context, info) => {
console.log("Way: id="+parent.userID)
user: Ways => getUserPerID(parent.userID)
}
You need to write:
Way: {
// We capitalize the field name User because that's what it is in your schema
User: (parent) => getUserPerID(parent.userID)
}

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!';
}
}

graphql conditional subquery based on parent field

What is the approach when your subquery needs some field from parent to resolve?
In my case owner_id field from parent is needed for owner resolver, but what should i do if user will not request it?
Should i somehow force fetch owner_id field or skip fetching owner when owner_id is not in request?
Problematic query:
project(projectId: $projectId) {
name
owner_id <--- what if user didn't fetch this
owner {
nickname
}
}
Graphql type:
type Project {
id: ID!
name: String!
owner_id: String!
owner: User!
}
And resolvers:
export const project = async (_, args, ___, info) => {
return await getProjectById(args.projectId, getAstFields(info))
}
export default {
async owner(parent, __, ___, info) {
return await getUserById(parent.owner_id, getAstFields(info))
},
}
Helper functions that doesn't matter:
export const getUserById = async (userId: string, fields: string[]) => {
const [owner] = await query<any>(`SELECT ${fields.join()} FROM users WHERE id=$1;`, [userId])
return owner
}
export const getAstFields = (info: GraphQLResolveInfo): string[] => {
const { fields } = simplify(parse(info) as any, info.returnType) as { fields: { [key: string]: { name: string } } }
return Object.entries(fields).map(([, v]) => v.name)
}
You can always read owner_id in project type resolver.
In this case ... modify getAstFields function to accept additional parameter, f.e. an array of always required properties. This way you can attach owner_id to fields requested in query (defined in info).
I was thinking that resolver will drop additional props internally.
It will be removed later, from 'final' response [object/tree].

GraphQL nested query returns null

I am trying to use a GraphQL nested query (I am 80% sure this is a nested query?) to get information on the listing and the chef (author) of the listing. I can get the listing info just fine, but I am unable to get the chef info.
I was under the impression that the default resolver (user) would fire when getListing(args) returned without a valid User object for the chef. But the default resolver does not appear to be firing.
How do I properly get the nested information?
For example, my query is:
query getListing($listingID: String!) {
getListing(listingID: $listingID) {
name
chef {
firstName
}
}
}
The query returns:
{
"data": {
"getListing": {
"name": "Test",
"chef": {
"firstName": null
}
}
}
}
The function getListing(args) queries the DB and returns:
{
name: 'Test',
chef: 'testUsername',
listingID: 'testListingID'
}
My Schema is:
type Listing {
uuid: String!
name: String!
chef: User!
}
type User {
username: String
firstName: String
}
type Query {
getUser(jwt: String!): User
getListing(listingID: String): Listing
}
And my resolvers are:
const resolvers = {
Query: {
getListing: async (parent, args, context, info) => {
console.log('GET_LISTING');
return getListing(args);
},
getUser: async (parent, args, context, info) => {
console.log('GET_USER');
return getUser(args);
},
},
User: async (parent, args) => {
console.log('USER RESOLVER');
return getUser(args);
},
};
Other Info:
I am using Apollo Server running on AWS Lambda integrating with DynamoDB on the backend.
Resolvers exist only at the field level. You can't resolve a type (i.e. User). You can only resolve a field that has that type (i.e. chef).
const resolvers = {
// ...
Listing: {
chef: (parent, args) => {
return getUser()
},
},
}
It's unclear what sort of parameters getUser accepts, so you'll need to modify the above example accordingly. You won't use args unless you actually specify arguments for the field being resolved in your schema. It looks like the returning listing has a chef property that's the name of the user, so you can access that value with parent.chef.

Nested GraphQL query with array argument

I am using apollo-datasource-rest with apollo-server-lambda and trying to figure out how to map a query to a specific resolver. I have the following schema, in which the plan query is supposed to return a list of users (that should be driven by the users query rather than the user query).
type Query {
user(user_id: String, username: String): User
users(user_ids: [String!]): [User!]
plan(plan_id: String): Plan
}
type User {
id: String
username: String
first: String
last: String
image: String
}
type Plan {
title: String
image: String
attending: [User]
}
The plan query resolver datasource is as follows:
planReducer(data) {
return {
image: data.public.info.image,
title: data.public.info.title,
attending: Object.keys(data.public.attending)
}
}
data.public.attending in the planReducer returns an array of user_ids that I would like to then be able to feed into my users query as opposed to my user query.
These are my current resolvers:
user: (_, { username }, { dataSources }) =>
dataSources.userData.getUserByUsername({ username: username }),
users: async (_, { user_ids }, { dataSources }) => {
const usersArray = await dataSources.userData.getUsersByIds({ userIds: user_ids })
return usersArray
},
plan: async (_, { plan_id }, { dataSources }) => {
return dataSources.planData.getPlanById({ planId: plan_id })
}
Your resolver map should look like below:
const resolvers = {
Query: {
plan: async (_parent, { plan_id: planId }, { dataSources }) => (
dataSources.planData.getPlanById({ planId })
)
},
Plan: {
users: async ({ user_ids: userIds }, _variables, { dataSources }) => (
dataSources.userData.getUsersByIds({ userIds })
)
}
}
Every key within Query should be a resolver that corresponds to a query defined within the root Query of your schema. Keys that are direct children of the root, in this case Plan, will be used to resolve their corresponding types when returned from the plan resolver within Query.
If resolvers are not defined, GraphQL will fall back to a default resolver which in this case looks like:
const resolvers = {
Plan: {
title: (parent) => parent.title,
image: (parent) => parent.image,
}
}
By specifying a custom resolver, you are able to compute fields to return to your clients based on the return value of parent resolvers.

Graphql find user by username or email

So I have this simple schema:
type User {
_id: String,
username: String,
age: Int,
email: String,
password: String,
}
type Query {
user(_id: String!): User,
}
Resolver:
import User from './User';
export default {
Query: {
user: (_, { _id }) => User.findById(_id),
},
}
how can I get a user by some specified fields like username, email without creating a query function for each one (if the user would have 1000 unique fields it would be pretty painful to create 1000 queries for each type) ?
If the names of your arguments match the names of your fields, it's easy enough to pass the entire args object as a parameter to find() or findOne():
Query: {
user: (_, args) => User.findOne(args),
},
In this case, you'll get the first user matching the arguments passed in and return it. In the event you want to return all possible matches, you can have two separate queries, one returning a single User, and another returning a List of Users:
Query: {
user: (_, args) => User.findOne(args),
users: (_, args) => User.find(args),
},
If there are any arguments that need to be treated differently (for example, if you wanted to pass in a limit or add some kind of pagination), you can always use a helper library like lodash or ramda to pull out just those arguments. For example:
users: (_, args) => {
const where = omit(args, ['limit'])
const limit = args.limit || 10
return User.find(where).sort({'someField': -1}).limit(limit).exec()
}
You can resolve your query like this
Query: {
user: (_, args) => User.findOne({
where: {
email: "Your search value"
}
}),
},
Query: {
user: (_, args) => User.findOne({
where: {
username: "Your search value"
}
}),
},
You can resolve it like this :
Query: {
async getPosts(_, { username }) {
try {
const posts = await Post.find({ username: username });
return posts;
} catch (err) {
throw new Error(err);
}
},
},

Resources