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.
Related
I want to create a GraphQL connection between User <> Role <> Role_Type and finally get the User Role Type back with a query. Here is brake it down to the only important lines of code:
type Query {
getUsers: [User]
}
type User {
_id: ID
firstname: String
roles: [Role]
}
type Role {
_id: ID
role_type_id: ID
role_types: [Role_Type]
user_id: ID
}
type Role_Type {
_id: ID
name: String
}
and in the User resolver I have:
Query: {
getUser: async (root, { _id }) => {
return prepare(await DBUsers.findOne(ObjectId(_id)))
}
},
User: {
roles: async ({_id}) => {
return (await MongoDBRoles.find({user_id: _id}).toArray()).map(prepare)
}
}
and for the Role resolver:
Role: {
role_types: async ({_id}) => {
return (await MongoDBRoleTypes.find({role_type_id: _id}).toArray()).map(prepare)
},
},
When I query now with:
{
getUser(_id: "5d555adcd2c22a242863f7a1") {
firstname
roles {
_id
role_type_id
user_id
role_types {
name
}
}
}
}
I get:
{
"data": {
"getUser": {
"firstname": "Gregor",
"roles": [
{
"_id": "5d90cf352f50882ab0ce3877",
"role_type_id": "5d90ce48b7893d19bcc328f9",
"user_id": "5d555adcd2c22a242863f7a1",
"role_types": []
}
]
}
}
}
But why is role_types empty. As you can see the role_type_id is filled. So why there is no connection.
When I watch into MongoDB I can see the Role Type of the user.
If you need more Schema/Resolver let me know.
Ok it was kind of easy to fix. Actually the Role resolver was going multiple matches. But somehow it needs to be single lookup. That does not make much sense but somehow the search for the id lookup is a single lookup. Anyway here is what I have changed and now it works properly.
Replace this:
role_types: async ({_id}) => {
return (await MongoDBRoleTypes.find({role_type_id: _id}).toArray()).map(prepare)
},
with:
role_type: async ({role_type_id}) => {
return prepare(await DBRoleTypes.findOne(ObjectId(role_type_id)))
},
and you fix this.
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.
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)
}
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);
}
},
},
Given a GraphQL schema and resolvers for Apollo Server, and a GraphQL query, is there a way to create a collection of all requested fields (in an Object or a Map) in the resolver function?
For a simple query, it's easy to recreate this collection from the info argument of the resolver.
Given a schema:
type User {
id: Int!
username: String!
roles: [Role!]!
}
type Role {
id: Int!
name: String!
description: String
}
schema {
query: Query
}
type Query {
getUser(id: Int!): User!
}
and a resolver:
Query: {
getUser: (root, args, context, info) => {
console.log(infoParser(info))
return db.Users.findOne({ id: args.id })
}
}
with a simple recursive infoParser function like this:
function infoParser (info) {
const fields = {}
info.fieldNodes.forEach(node => {
parseSelectionSet(node.selectionSet.selections, fields)
})
return fields
}
function parseSelectionSet (selections, fields) {
selections.forEach(selection => {
const name = selection.name.value
fields[name] = selection.selectionSet
? parseSelectionSet(selection.selectionSet.selections, {})
: true
})
return fields
}
The following query results in this log:
{
getUser(id: 1) {
id
username
roles {
name
}
}
}
=> { id: true, username: true, roles: { name: true } }
Things get pretty ugly pretty soon, for example when you use fragments in the query:
fragment UserInfo on User {
id
username
roles {
name
}
}
{
getUser(id: 1) {
...UserInfo
username
roles {
description
}
}
}
GraphQL engine correctly ignores duplicates, (deeply) merges etc. queried fields on execution, but it is not reflected in the info argument. When you add unions and inline fragments it just gets hairier.
Is there a way to construct a collection of all fields requested in a query, taking in account advanced querying capabilities of GraphQL?
Info about the info argument can be found on the Apollo docs site and in the graphql-js Github repo.
I know it has been a while but in case anyone ends up here, there is an npm package called graphql-list-fields by Jake Pusareti that does this. It handles fragments and skip and include directives.
you can also check the code here.