how to return token from graphql mutation - jwt

This is my code of mutation in which i use the type of User which has name,email,password and i make two mutations for the register the user and login the user. I have searched all the docs about the graphql and read all the blogs related to authentication but can't got the answer to return the token from the mutation
const mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addUser: {
type: UserType,
args: {
name: { type: GraphQLString },
email: { type: GraphQLString },
password: { type: GraphQLString },
avatar: { type: GraphQLString }
},
resolve(parentValue, args) {
const avatar = gravatar.url(args.email);
return bcrypt
.hash(args.password, 10)
.then(hash => {
args.password = hash;
const newUser = new User({
name: args.name,
email: args.email,
password: args.password,
avatar
});
return newUser
.save()
.then(user => user)
.catch(e => e);
})
.catch(e => e);
}
},
login: {
name: "Login",
type: UserType,
args: {
email: { type: GraphQLString },
password: { type: GraphQLString }
},
resolve(parentValue, args, context) {
return User.findOne({ email: args.email })
.then(user => {
if (user) {
return bcrypt
.compare(args.password, user.password)
.then(isValid => {
if (!isValid) {
throw new Error({ message: "password Incrrect" });
} else {
const token = jwt.sign(
{ name: user.name, id: user.id },
"mySecret"
);
return user;
}
})
.catch(e => e);
} else {
throw new Error({ message: "email Incorrect" });
}
})
.catch(e => e);
}
}
}
});
This is my User Type
const UserType = new GraphQLObjectType({
name: "User",
fields: {
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
password: { type: GraphQLString },
avatar: { type: GraphQLString }
}
});

I would advise you to update your UserType by removing the password field and add a token field like:
const UserType = new GraphQLObjectType({
name: "User",
fields: {
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
avatar: { type: GraphQLString },
token: { type: GraphQLString }
}
});
The reason is that UserType is a return type of the mutation, so it's "public", and maybe we should not send a password to public(as we are authenticating on the server side), but JWT is public, so we can send it back.
And in your login mutation add the token into the user object, something like:
login: {
name: "Login",
type: UserType,
args: { email: { type: GraphQLString }, password: { type: GraphQLString } },
resolve(parentValue, args, context) {
return User.findOne({ email: args.email })
.then(user => {
.........
const token = jwt.sign(
{ name: user.name, id: user.id },
"mySecret"
);
user.token = token;
return user;
}
........
}
}

You can create different type for mutation like this without changing the existing UserType.
const UserMutationType = new GraphQLObjectType({
name: "User",
fields: {
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
avatar: { type: GraphQLString },
token: { type: GraphQLString }
}
});
And add this type in mutation
login: {
name: "Login",
type: UserMutationType,
args: {},
resolver(parent,args,context){},
}

Related

How to I resolve below graphql query in mongodb nested array

my model schema look like this
const mongoose = require("mongoose")
const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
select: false,
},
email: {
type: String,
required: true,
unique: true,
match: [
/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/,
"Please enter a valid email",
],
},
followers:[
{
type:mongoose.Schema.Types.ObjectId,
ref:"user"
}
],
following:[
{
type:mongoose.Schema.Types.ObjectId,
ref:"user"
}
],
displayName: {
type: String,
required: false,
},
},
{ timestamps: true }
)
module.exports = mongoose.model("user", userSchema)
in this schema all working good like mutation work fine but when i fetch query of all user then in that query followers and following field return null like bellow image
and my graphql query is
const users = {
type: new GraphQLList(UserType),
description: "Retrieves list of users",
resolve(parent, args) {
return User.find()
},
}
and typedef is
const UserType = new GraphQLObjectType({
name: "User",
description: "User types",
fields: () => ({
id: { type: GraphQLID },
username: { type: GraphQLString },
email: { type: GraphQLString },
post:{
type: GraphQLList(PostType),
resolve(parent, args) {
return Post.find({ authorId: parent.id })
},
},
savePost:{
type:GraphQLList(savedPosts1),
resolve(parent, args) {
return SavePost.findById({ authorId: parent.id })
},
},
followers:{
type:GraphQLList(UserType),
},
following:{
type:GraphQLList(UserType)
}
// displayName: { type: GraphQLString },
}),
})
so please tell me how to i resolve that followers and following query in graphql with mongodb and tell me what i write in my userType typedef

How to update a user profile which has a property which is a ref in MongooseJS?

I have a User schema which has reference to a profile schema.
const UserSchema = new Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
....email props...
},
password: {
...password props...
},
profile: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Profile",
}],
},
);
const Profile = new Schema({
_user: {
type: Schema.Types.ObjectId, ref: 'User'
},
'displayName': {
type: String,
default: ''
},
'interestedActivities': ['Ping-pong'], <---- This bad boy/girl is an array
'memberSince': { type: Date, default: Date.now }
}
)
I'd like to create a route which can update the User properties AND the Profile properties in one shot—with a caveat one of the properties on the Profile model is an array!!!
I tried this....
handler
.use(auth)
.put((req, res, next) => {
emailValidator(req, res, next, 'email');
},
async (req, res, next) => {
await connectDB()
const {
profileDisplayName,
profileEmail,
interestedActivities } = req.body;
const update = {
email: profileEmail,
'profile.$.displayName': profileDisplayName,
'profile.$.interestedActivities': interestedActivities
}
const filter = { _id: req.user.id };
const updatedUser = await User.findOneAndUpdate(filter, update, { new: true })
try {
console.log("updatedUser ", updatedUser);
if (updatedUser) {
return res.status(200).send({
updatedUser,
msg: `You have updated your profile, good job!`
});
}
} catch (error) {
errorHandler(error, res)
}
})
export default handler;
My response is:
Status Code: 500 Internal Server Error
Cast to ObjectId failed for value "[
{
id: 'ae925393-0935-45da-93cb-7db509aedf20',
name: 'interestedActivities',
value: []
}
]" (type Array) at path "profile.$"
Does anyone know how I could also afford for the property which is an array?
Thank you in advance!

Why graphql type relations returns null values?

i have this schema.js of SHIPS
// Ships
const ShipType = new GraphQLObjectType({
name: "Ships",
fields: () => ({
ship_id: { type: GraphQLString },
ship_name: { type: GraphQLString },
home_port: { type: GraphQLString },
ship_type: { type: GraphQLString },
year_built: { type: GraphQLInt },
position: { type: ShipPositionType },
active: { type: GraphQLBoolean }
})
})
// Ship positions
const ShipPositionType = new GraphQLObjectType({
name: "ShipPositions",
fields: () => ({
latitude: { type: GraphQLFloat },
longitude: { type: GraphQLFloat }
})
});
at ShipType i have added a new field "position" which will return ShipPositionType
this is what i return so far
// get ships
ships: {
type: new GraphQLList(ShipType),
resolve(parent, args) {
return axios.get('https://api.spacexdata.com/v3/ships')
.then(res => res.data);
}
}
The position field inside the shipType need to have a resolver, inside the resolver you return the ship data based on the ShipType ship_id matching the ShipPositionType ship_id. i.e(return data.find(ship => ship.shid_id === parent.ship_id )) . So first add a ship_id field to the ShipPositionType Object.
Something like this
// Ships
const ShipType = new GraphQLObjectType({
name: "Ships",
fields: () => ({
ship_id: { type: GraphQLString },
ship_name: { type: GraphQLString },
home_port: { type: GraphQLString },
ship_type: { type: GraphQLString },
year_built: { type: GraphQLInt },
position: {
type: ShipPositionType,
resolve(parent,args) {
return data.find(data => data.ship_id === parent.ship_id)}
},
active: { type: GraphQLBoolean }
})
})
//You need to add a ship_id field here too
// Ship positions
const ShipPositionType = new GraphQLObjectType({
name: "ShipPositions",
fields: () => ({
ship_id: {type: {GraphQLString},
latitude: { type: GraphQLFloat },
longitude: { type: GraphQLFloat }
})
});

Graphql create relations between two queries.Error cannot access before initialization

I have this code:
const ProductType = new GraphQLObjectType({
name: 'Product',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
category: {
type: CategoryType,
resolve: async (parent) => {
return await Category.findOne({_id: parent.category});
}
}
}
});
const CategoryType = new GraphQLObjectType({
name: 'Category',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
products: {
type: ProductType,
resolve: async (parent, args) => {
return await Product.find({category: parent._id});
}
}
}
});
const Query = new GraphQLObjectType({
name: 'Query',
fields: {
Categories: {
type: new GraphQLList(CategoryType),
resolve: async () => {
return await Category.find();
}
}
}
});
When i try to compile i get ReferenceError: Cannot access 'CategoryType' before initialization.
I understand that first of all I should declare and only after that use, but I saw a similar code in one lesson on YouTube, and I think that it should work, but it’s not.
fields can take a function instead of an object. This way the code inside the function won't be evaluated immediately:
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
category: {
type: CategoryType,
resolve: (parent) => Category.findOne({_id: parent.category}),
}
})

trying to connect graphql to postgress how to define user and pass?

I fail to receive information from my postgres db when trying to connect with graphql.
I receive the following response:
{
"errors": [
{
"message": "password authentication failed for user \"admin\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"account"
]
}
],
"data": {
"account": null
}
}
I honestly don't know where to define the user and pass.
const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema');
const app = express();
app.use('/graphql', expressGraphQL({
schema,
graphiql: true
}))
app.listen(4000, () => {
console.log('Listening...')
})
and this is my schema file
const graphql = require('graphql');
const connectionString = 'myURI';
const pgp = require('pg-promise')();
const db = {}
db.conn = pgp(connectionString);
const {
GraphQLObjectType,
GraphQLID,
GraphQLString,
GraphQLBoolean,
GraphQLList,
GraphQLSchema
} = graphql;
const AccountType = new GraphQLObjectType({
name: 'account',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
busines_name: { type: GraphQLString },
email: {
type: new GraphQLList(EmailType),
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE
account=${parentValue.id}`;
return db.conn.many(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
})
})
const EmailType = new GraphQLObjectType({
name: 'Email',
fields: {
id: { type: GraphQLID },
email: { type: GraphQLString },
primary: { type: GraphQLBoolean }
}
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
account: {
type: AccountType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "account" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
},
emails: {
type: EmailType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
}
})
module.exports = new GraphQLSchema({
query: RootQuery
})
I would like to know where to define the user and the password for the db of what i'm doing wrong besides that.
const connectionString = 'myURI';
It should be enough if your connection string includes the username and password. Is your DB connection string of the form postgres://username:password#server:5432 ?
See https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING