How can I get user by userName in GraphQL? - graphql

I have this RootQuery:
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: UserType,
args: { id: { type: GraphQLID } },
resolve(parent, {id}) {
return User.findById(id)
}
},
and then Ill use this query to get the user:
{
user(id:"5bd78614e71a37341cd2b647"){
id
userName
password
isAdmin
}
}
it works just fine' now i dont want to get the user by his ID,
I want to get him by his userName insted so I used this
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: UserType,
args: { userName: { type: GraphQLString } },
resolve(parent, {userName}) {
console.log('userName',userName)
return User.find({ userName })
}
},
this will bring back the user with all properties be null
please help!!

First, you should use findOne to get only one user, find will bring you all users with that name. If you want that, maybe your return should be type: GraphQLList(UserType).
If it's bringing all properties it is probably because you are asking for them on the query.
Also, you might be missing an await User.find({ userName }), and an async on your function:
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: UserType,
args: { userName: { type: GraphQLString } },
resolve: async (parent, {userName}) => {
console.log('userName',userName)
const user = await User.findOne({ userName })
console.log('user',user);
return user;
}
},
Check if this helps you :)

Related

Prisma cannot create a user type because of array argument?

this is my InputType in schema.graphql:
input RegisterInput {
birthday: String!
email: String!
firstName: String!
gender: String!
interests: [String!]!
lastName: String!
password: String!
}
and this is my mutation:
const RegisterInput = inputObjectType({
name: 'RegisterInput',
definition(t) {
t.string('birthday', { nullable: false });
t.string('email', { nullable: false });
t.string('firstName', { nullable: false });
t.string('lastName', { nullable: false });
t.string('gender', { nullable: false });
t.string('password', { nullable: false });
t.list.field('interests', {
type: 'String',
nullable: false,
});
},
});
const Mutation = objectType({
name: 'Mutation',
definition(t) {
t.field('register', {
type: User,
args: {
data: arg({ type: RegisterInput }),
},
resolve: async (
_root,
{ data: { password, interests, ...userData } },
{ prisma }
) => {
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.create({
data: {
...userData,
interests: [...interests],
password: hashedPassword,
},
});
return user;
},
});
my interests is just an array of strings, .e.g: ['abcd', 'def']
but i got this error:
Unknown arg `0` in data.interests.0 for type UserCreateInterestInput. Available args:
type UserCreateInterestsInput {
set?: List<String>
}
that error will repeat depending of how many items is in the array, e.g.: Unknown arg '1' and so on, same error message, how do i fix this?
You must provide a list of strings to set argument, such as:
type UserCreateInterestsInput {
set?: List<String>
}
Refer to this issue for more information.
const Mutation = objectType({
name: 'Mutation',
definition(t) {
t.field('register', {
type: User,
args: {
data: arg({ type: RegisterInput }),
},
resolve: async (
_root,
{ data: { password, interests, ...userData } },
{ prisma }
) => {
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.create({
data: {
...userData,
interests: {set: interests},
password: hashedPassword,
},
});
return user;
},
});
Hope this helps
Happened to me earlier, turns out it was a query mistake.
mutation {
createFruit(data:{
name: "Banana",
images: {
set: ["image_1.img", "image_2.img"]
}
}) {
name
images
}
}
Note it's not images: ["image_1.img", "image_2.img"]
fyi with prisma you can do t.model.interest() when defining objectType

GraphQL Subscriptions Error: "The \"properties\" argument must be of type Array. Received type object"

I am trying to implement a simple API with GraphQL. My queries and my mutations are in place and working, but now I'm trying to include subscriptions as well.
I already added the subscription in the schema, I included the event publish in the addUser mutation and defined the subscribe function for the subscription type.
Now, when I am trying to run a subscription query in the graphiql in-browser IDE, I get this error:
"The \"properties\" argument must be of type Array. Received type object"
Attached is the schema object. Did I configured something wrong or am I missing something? Thanks!
P.S I also need to mention that I am using mongoose to store the data on an a mongo instance, hence the entities.
import {
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
// models
import UserType from '../types/user/UserType';
import AccountType from '../types/account/AccountType';
import TransactionType from '../types/transaction/TransactionType';
// entities
import User from '../entities/user/user';
import Account from '../entities/account/account';
import Transaction from '../entities/transaction/transaction';
// subscriptions
import { PubSub } from 'graphql-subscriptions';
// subscriptions
const pubsub = new PubSub();
const USER_CREATED = 'user_created';
// the acceptable starting point of our graph
const RootQueryType = new GraphQLObjectType({
name: 'RootQueryType',
fields: () => ({
// query individual entities in the database
user: {
type: UserType,
description: 'The current user identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return User.findById(args.id);
}
},
account: {
type: AccountType,
description: 'Details about the account in question identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Account.findById(args.id);
}
},
transaction: {
type: TransactionType,
description: 'Details about the transaction in question identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Transaction.findById(args.id);
}
},
// query all entities in the database
users: {
type: new GraphQLList(UserType),
resolve: (parent, args) => {
return User.find({});
}
},
accounts: {
type: new GraphQLList(AccountType),
resolve: (parent, args) => {
return Account.find({});
}
},
transactions: {
type: new GraphQLList(TransactionType),
resolve(parent, args) {
return Transaction.find({});
}
}
})
});
const MutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
addUser: {
type: UserType,
args: {
name: {
type: new GraphQLNonNull(GraphQLString)
},
age: {
type: new GraphQLNonNull(GraphQLInt)
},
email: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(parent, args) {
let user = new User({
name: args.name,
age: args.age,
email: args.email
});
pubsub.publish(USER_CREATED, {
newUser: user
});
return user.save();
}
},
addAccount: {
type: AccountType,
args: {
currency: {
type: new GraphQLNonNull(GraphQLString)
},
balance: {
type: new GraphQLNonNull(GraphQLFloat)
},
holderId: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(parent, args) {
let account = new Account({
currency: args.currency,
balance: args.balance,
holderId: args.holderId
});
return account.save().then(() => console.log('user created'));
}
},
addTransaction: {
type: TransactionType,
args: {
sourceAccountId: {
type: new GraphQLNonNull(GraphQLString)
},
targetAccountId: {
type: new GraphQLNonNull(GraphQLString)
},
amount: {
type: new GraphQLNonNull(GraphQLFloat)
}
},
resolve(parent, args) {
let transaction = new Transaction({
sourceAccountId: args.sourceAccountId,
tagetAccountId: args.tagetAccountId,
timestamp: new Date(),
amount: args.amount
});
Account.findById(args.sourceAccountId, (err, account) => {
if (!err) {
account.balance -= args.amount;
return account.save();
}
});
Account.findById(args.targetAccountId, (err, account) => {
if (!err) {
account.balance += args.amount;
return account.save();
}
});
return transaction.save();
}
}
})
});
const SubscriptionType = new GraphQLObjectType({
name: 'Subscription',
fields: () => ({
newUser: {
type: UserType,
description: 'This subscription is going to provide information every time a new user creation event fires',
resolve: (payload, args, context, info) => {
console.table(payload, args, context, info); // debugging
return payload;
},
subscribe: () => pubsub.asyncIterator(USER_CREATED)
}
})
});
const schema = new GraphQLSchema({
query: RootQueryType,
mutation: MutationType,
subscription: SubscriptionType
});
export default schema;
I expect that when I run the subscription query, it will run listening for events being published and when from another tab I will run a mutation to add a new user, the first tab will catch the event and return details of the user in the payload.

How to change property values for an object nested in an array in graphql?

I've just started to learn GraphQL recently and have decided to implement it in a react based polling app where users can create and vote on polls.
I've created a mongoose model that looks like this https://github.com/luckyrose89/Voting-App/blob/master/backend/models/poll.js.
I'm facing an issue with adding upvotes to a poll option while writing Graphql mutations. So far my schema looks like this:
const AnswerType = new GraphQLObjectType({
name: "Answer",
fields: () => ({
id: { type: GraphQLID },
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionType = new GraphQLObjectType({
name: "Question",
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLID) },
question: { type: GraphQLString },
answer: { type: GraphQLList(AnswerType) }
})
});
const AnswerTypeInput = new GraphQLInputObjectType({
name: "AnswerInput",
fields: () => ({
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionTypeInput = new GraphQLInputObjectType({
name: "QuestionInput",
fields: () => ({
question: { type: new GraphQLNonNull(GraphQLString) },
answer: { type: new GraphQLNonNull(GraphQLList(AnswerTypeInput)) }
})
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addPoll: {
\\\\ code here
},
deletePoll: {
\\\\\ code here
},
upvotePoll: {
type: QuestionType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parent, args) {}
}
}
});
So I've defined my types and I can add and delete polls and access a single poll(I've skipped my queries section here). But I don't understand how to access a single poll's AnswerType object without retrieving unnecessary data and use it to write my upVote mutation.
I hope someone can guide me with this

GraphQL mutation structure

I am trying to create a Node.js graphql server in Typescript. I am using Express and express-graphql. I have some issues with how to structure my mutation when I want to create a new User.
My goal is to be able to use a mutation like this:
mutation {
user {
create(
data: {
name: "Foo Bar"
}
) {
id,
name
}
}
}
Here is my User types:
import {
GraphQLObjectType,
GraphQLNonNull,
GraphQLBoolean,
GraphQLString,
GraphQLInputObjectType
} from 'graphql';
export const UserType = new GraphQLObjectType({
name: 'User',
description: 'A user of the application',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'The id of the user',
},
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the user',
}
})
});
export const UserInputType = new GraphQLInputObjectType({
name: 'UserInputType',
description: 'User payload definition',
fields: () => ({
name: { type: new GraphQLNonNull(GraphQLString) }
})
});
Here is my attempt at defining the mutation on the server:
// ../user/user-mutations.ts
export const userMutations = {
user: {
type: new GraphQLObjectType({
name: 'CreateUser',
fields: {
create: {
type: UserType,
args: {
data: {
type: new GraphQLNonNull(UserInputType),
}
},
resolve: async (rootValue, { data }) => {
return Object.assign(data, {
id: '123'
});
}
}
}
})
}
};
My errors/output:
{
"errors": [
{
"message": "Cannot convert undefined or null to object",
"locations": [
{
"line": 36,
"column": 3
}
],
"path": [
"user"
]
}
],
"data": {
"user": null
}
}
Question 1: Is this way of structuring a mutation not optimal? Should I rather do something like:
mutation {
createUser(
name: "Foo Bar"
) {
id,
name
}
}
Question 2: If my first structure is fine, how can I fix the structure of my mutation on the server to create my user and return the values requested?
Edit: Here is my top level schema:
import { userQueries } from '../user/user-queries';
export const queries = {
...userQueries
};
import { userMutations } from '../user/user-mutations';
export const mutations = {
...userMutations
};
const rootQuery = new GraphQLObjectType({
name: 'RootQuery',
fields: queries
});
const rootMutation = new GraphQLObjectType({
name: 'RootMutation',
fields: mutations
});
export const schema = new GraphQLSchema({
query: rootQuery,
mutation: rootMutation
});

GraphQL mutation without sub section

I want to send graphql mutation request without sub section
mutation _ {
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com")
}
and I am getting
{
"errors": [
{
"message": "Field \"updateCurrentUser\" of type \"User\" must have a sub selection.",
...
}
]
}
add { id } to request works fine but I don't want
Also Schema code
const userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLString) },
fullName: { type: GraphQLString },
email: { type: GraphQLString },
}),
});
type: userType,
args: {
fullName: { type: GraphQLString },
email: { type: new GraphQLNonNull(emailType) },
password: { type: GraphQLString },
},
resolve: async (root, { fullName, email, password }, { rootValue }) => {
const user = await User.findById(rootValue.req.user.id);
...
return user;
},
You define the type of the field to be UserType. Even though it's a mutation, it still follows the same rules and behavior as a query. Because UserType is an object type, it requires nested fields.
mutation _ {
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com") {
fullName
email
}
}
// would respond with { fullName: 'Syava', email: 'fake#gmail.com' }
If you don't want the mutation to return a User, you can declare its type to GraphQLBoolean for example -- that's a scalar and doesn't have any nested fields.
{
type: GraphQLBoolean,
args: {
fullName: { type: GraphQLString },
email: { type: new GraphQLNonNull(emailType) },
password: { type: GraphQLString },
},
resolve: async (root, { fullName, email, password }, { rootValue }) => {
const user = await User.findById(rootValue.req.user.id);
user.fullName = fullName;
user.password = password; // or hashed to not store plain text passwords
return user.save(); // assuming save returns boolean; depends on the library you use
}
}
Note that the best practice for mutations in GraphQL APIs is to return a "result" object with multiple fields, such as the mutated object itself (e.g. user), clientMutationId (per Relay spec), and others as needed. This makes it flexible so you could add more data in the future.
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com") {
clientMutationId
user {
...
}
}

Resources