Graphql find user by username or email - graphql

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);
}
},
},

Related

How to define graphql query in schema with exactly one of two parameters required [duplicate]

I'm just getting to grips with GraphQL,
I have set up the following query:
​
type: UserType,
args: {
id: { name: 'id', type: new GraphQLNonNull(GraphQLID) },
email: { name: 'email', type: new GraphQLNonNull(GraphQLString) }
},
resolve: (root, { id, email }, { db: { User } }, fieldASTs) => {
...
}
I would like to be able to pass either an 'id' or 'email' to the query, however, with this setup it requires both an id and email to be passed.
Is there a way to set up the query so only one argument is required, either id or email, but not both?
There's no built-in way to do that in GraphQL. You need to make your arguments nullable (by removing the GraphQLNonNull wrapper type from both of them) and then, inside your resolver, you can just do a check like:
resolve: (root, { id, email }, { db: { User } }, fieldASTs) => {
if (!id && !email) return Promise.reject(new Error('Must pass in either an id or email'))
if (id && email) return Promise.reject(new Error('Must pass in either an id or email, but not both.'))
// the rest of your resolver
}
Define an interface credentials and have that implemented as id or email.

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 resolvers only resolving the first type

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)
}

How do I set up GraphQL query so one or another argument is required, but not both

I'm just getting to grips with GraphQL,
I have set up the following query:
​
type: UserType,
args: {
id: { name: 'id', type: new GraphQLNonNull(GraphQLID) },
email: { name: 'email', type: new GraphQLNonNull(GraphQLString) }
},
resolve: (root, { id, email }, { db: { User } }, fieldASTs) => {
...
}
I would like to be able to pass either an 'id' or 'email' to the query, however, with this setup it requires both an id and email to be passed.
Is there a way to set up the query so only one argument is required, either id or email, but not both?
There's no built-in way to do that in GraphQL. You need to make your arguments nullable (by removing the GraphQLNonNull wrapper type from both of them) and then, inside your resolver, you can just do a check like:
resolve: (root, { id, email }, { db: { User } }, fieldASTs) => {
if (!id && !email) return Promise.reject(new Error('Must pass in either an id or email'))
if (id && email) return Promise.reject(new Error('Must pass in either an id or email, but not both.'))
// the rest of your resolver
}
Define an interface credentials and have that implemented as id or email.

Resources