when I create a user in mongoose and I don't give my email, I get an error, but if I don't provide the password, nothing shows up and the user is saved to the database. in both cases I use: required: [true, "error "]
image
did you add validation for the password field? I do my user model like this:
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
gender: {
type: String,
enum: ['male', 'female'],
},
profilePicture: {
type: String
},
password: {
type: String,
required: true,
},
email: {
type: String,
unique: true,
required: true,
},
}, {
timestamps: true
}, );
// hash the password before the user is saved
UserSchema.pre('save', function hashPassword(next) {
// hash the password only if the password has been changed or user is new
if (!this.isModified('password')) {
next();
return;
}
// generate the hash
_hash(this.password, null, null, (err, hash) => {
if (err) {
next(err);
return;
}
// change the password to the hashed version
this.password = hash;
next();
});
});
// method to compare a given password with the database hash
UserSchema.methods.comparePassword = function comparePassword(password) {
const data = compareSync(password, this.password);
return data;
};
I hope this helps :)
Have you tried adding minlength? Maybe trim would help?
https://mongoosejs.com/docs/schematypes.html#string-validators
email: {
type: String,
required: [true, 'Please provide an email.'],
minlength: 1,
maxlength: 25,
trim: true,
unique: true,
},
Related
So I'm having trouble authenticating a user login using express for the backend. If I do a simple res.send I could get a response in postman. but if I do a check if the user and password check and generate a token if says error 401 invalid usernames and password. mind the tokens work for register and update profiles. I also attached the user schema.
Simple Approach
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
res.send({ email, password })
})
When I try to check and generate a token
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid Email or password')
}
})
Also here's my user schema using mongoose and added a function to check password since in the database it's encrypted using bcrypt.js
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
You see, your code is absolutely correct, but only if you have saved the user correctly in your model at the first. In addition, the password used during storage is hashed in the same way and from the same module.
Now you have to change your checking terms because there may not really be a user with this email in the database to match your password and return true value.
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', function (next) {
if (this.password) {
bcrypt.hash(this.password, bcrypt.genSaltSync(15), (err, hash) => {
if (err) console.log(err);
this.password = hash;
next();
});
}
}
userSchema.methods.Save = async (data) => {
let model=new User(data)
await model.save();
}
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
and update this code:
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user) {
if(await user.matchPassword(password)) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid password')
}
} else {
res.status(401)
throw new Error('Invalid Email')
}
})
Now you know which part of the job is the problem. No user or no wrist password
I'm trying to create a database with a foreign key but I kept getting the error of no such column: created_at, even tho I'm not using created_at somewhere in my code. I wonder why and how to patch this issue.
var User = sequelize.define('user', {
username: Sequelize.STRING,
myhash: Sequelize.TEXT,
mysalt: Sequelize.STRING,
keyword: Sequelize.STRING,
})
, Company = sequelize.define('company', {
keyword: Sequelize.STRING
});
User.belongsTo(Company);
var User = sequelize.define('user', {
username: Sequelize.STRING,
myhash: Sequelize.TEXT,
mysalt: Sequelize.STRING,
keyword: Sequelize.STRING,
}, {underscored: true})
, Company = sequelize.define('company', {
uuid: {
type: Sequelize.UUID,
primaryKey: true
}
});
User.belongsTo(Company);
created_at is automatically created for keeping the timestamp. You need to set the timestamp option to false like below to all your model definitions.
var User = sequelize.define('user', {
username: Sequelize.STRING,
myhash: Sequelize.TEXT,
mysalt: Sequelize.STRING,
keyword: Sequelize.STRING,
},{
timestamps: false
})
Am working with mongoose and have two models. The User model and the Service model, when a user logs in the method will findOne() user if one exists or create() a new user based on the what's passed in from req.body.
My Service Schema is like this:
const serviceSchema = new mongoose.Schema({
name: {
type: String,
default: 'contentEditor'
},
display: {
type: String,
default: 'Content Editor'
},
accessLevel: {
type: Number,
min: 0,
max: 4,
default: 4
}
});
My User Schema is a bit bigger, I've removed some of the field/value pairs but the part where I embed the Service Schema looks like this:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: [true, 'Must have a email address'],
trim: true,
unique: true,
},
firstName: {
type: String,
},
lastName: {
type: String,
},
services: {
type: [serviceSchema],
ref: 'Services',
default: [serviceSchema],
},
},
);
When I hit the /api/v1/login endpoint a new user will be created with the Service document correctly but within the Mongoose database only a User collection exists. How do I make it so that both a Users collection and Services collection are created?
Edit: Below is the function that I create/find the user with when they login. When an existing User is found, by their email it will return that user if the user is not found then it will create a new one...
Both behaviours are as expected including adding the Services to the newly created User. What isn't expected is that only ONE collection is added to the DB.
const login = catchAsync(async ({ body: { email, password } }, res, next) => {
if (!email || !password) {
return next(new AppError('Please provide email and password', 400));
}
const { Success } = await webApi(email, password);
const mongoUser = await User.findOne({ email });
if (Success && mongoUser) {
return createSendtoken(mongoUser, 200, res);
}
if (Success && !mongoUser) {
const newUser = await User.create({ email });
return createSendtoken(newUser, 201, res);
}
return next(new AppError('User not found', 404));
});
Make sure you are making the serviceSchema a mongoose model.
const Services = mongoose.model('Service', serviceSchema)
You also have to save it using mongooses model.save() function
I'm using ExpressJS + Mongoose + TypeScript. I have created a schema as below
const schema: Schema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
gender: {
type: Boolean,
required: true
},
mobile: {
type: String,
required: false
},
password: {
type: String,
required: true
},
activationKey: {
type: String,
required: false
},
isActivated: {
type: Boolean,
required: true
},
imagePath: {
type: String,
required: false
},
});
I'm using the below code to save (insert) a new entry
MongoClient.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', (err, db) => {
if (err) {
console.log('mongoose error: ' + err);
} else {
console.log('mongoose db: ' + db);
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
Console Messages
When correct data is sent. saved isn't printed
mongoose db: [object Object]
When incorrect data is sent
mongoose db: [object Object]
not saved:ValidationError: gender: Path `gender` is required.
When unable to connect to MongoDB if internet is disconnected
mongoose error: MongoError: failed to connect to server [ds135777.mlab.com:35777] on first connect [MongoError: connection 0 to ds135777.mlab.com:35777 timed out]
Module versions
"mongodb": "^2.2.34",
"#types/mongodb": "^3.0.5",
"mongoose": "^5.0.4",
"#types/mongoose": "^5.0.2",
Re-written example (Solution)
const mongoose = require('mongoose');
mongoose.connect('mongodb://MyUsername:MyPassword#ds135757.mlab.com:35777/my-db-us', function(err) {
if (err) {
console.log('err: ' + err);
} else {
console.log('connected');
const user = new User({
firstName: 'ee',
lastName: 'ee',
email: 'eee#fff.com',
gender: true,
mobile: '333',
password: '333',
isActivated: true
});
user.save().then((someValue) => {
console.log('saved');
}).catch((err) => {
console.log('not saved:' + err);
});
}
});
The messages printed are
connected
saved
I am guessing a funny problem with your code(Just guessing looking at your variable name convention ;-)).
You are saying you use mongoose but you connect using native MongoClient (again guessing based on variable name) you must be connecting using mongoose
const mongoose = require('mongoose');
then just replace your MongoClient with mongoose
then doesn't print anything as nothing is happening there and catch prints error as validation happens before connection
The reason is you are using native client for connecting and using mongoose for modelling which is not the correct way. Do connect to the Mongo DB URI using mongoose and save schema.
You are creating a standard MongoClient connection, this will not effect mongoose models. The connection that created the User model must be open for the various database actions to work. Assuming that the User model was created using the global mongoose object (e.g. mongoose.model('User', userSchema)) then you must call mongoose.connect() to activate the model's connection. If the User model was created via a non-global connection (e.g. mongoose.createConnection()) then you should ensure that the connection is in the open state.
I have been having SO much trouble trying to get a mutation to work.
Given this GraphQL Schema, can anyone PLEASE help me create a simple create User mutation? I don't understand what I am missing. I got it to a point where it throws a 400 error from the GraphQL server and it does not fire the resolve function.
var userType = new GraphQLObjectType({
name: 'User',
description: 'User creator',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'The id of the user.'
},
email: {
type: GraphQLString,
description: 'The email of the user.'
},
business: {
type: GraphQLString,
description:
'The name of the business of the user as the app refers to it.'
},
businessDisplayName: {
type: GraphQLString,
description: 'The name of the business of the user as they typed it in.'
},
trips: {
type: new GraphQLList(tripType),
description: 'The trips of the user, or an empty list if they have none.',
resolve: (user, params, source, fieldASTs) => {
var projections = infoToProjection(fieldASTs)
return Trip.find(
{
_id: {
// to make it easily testable
$in: user.trips.map(id => id.toString())
}
},
projections,
function(err, docs) {
return docs
}
)
}
}
})
})
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'root',
fields: {
trips: {
type: new GraphQLList(tripType),
resolve: function() {
return Trip.find({})
}
},
users: {
type: new GraphQLList(userType),
resolve: function() {
return User.find({})
}
},
user: {
type: userType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }, source, fieldASTs) => {
return User.findOne(
{ _id: id },
infoToProjection(fieldASTs),
function(err, doc) {
return doc
}
)
}
},
trip: {
type: tripType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }, source, fieldASTs) => {
var projections = infoToProjection(fieldASTs)
return Trip.findOne({ _id: id }, projections, function(err, doc) {
return doc
})
}
}
}
}),
// mutation
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: {
createUser: {
name: 'createUser',
type: userType,
args: {
input: { type: new GraphQLInputObjectType({
name: 'user',
fields: {
business: { type: GraphQLString },
email: { type: GraphQLString },
businessDisplayName: { type: GraphQLString }
}
})
}},
resolve: (parentValue, args) => {
let user = new User({ ...args.input })
user.save()
return user
}
}
})
})
export var getProjections = infoToProjection
export default schema
This works with GraphiQL using the following queries or mutations:
mutation {
createUser(input:{business:"business", email: "e#mai.l", businessDisplayName: "businessDN"}) {
id
email
business
businessDisplayName
}
}
fragment UserFragment on User {
id
business
businessDisplayName
trips{
title
}
}
{
hideya: user(id: "someid") {
...UserFragment
}
}
I finally fixed the problem. Tried to understand the source of the problem so I used a new NetworkLayer to enable appropriate logging and meaningful error messages. Then threw the an error when my mutation failed. The error message was : "Cannot query field clientMutationId". Looked that up and found that to be able to mutate objects you need to have that field on your GraphQL type. So I added it.
Lesson learned: I highly recommend using react-relay-network-layer.
More details:
Here is my code for it:
import {
RelayNetworkLayer,
urlMiddleware,
batchMiddleware,
} from 'react-relay-network-layer';
Relay.injectNetworkLayer(new RelayNetworkLayer([
batchMiddleware({
batchUrl: 'http://localhost:3000/graphql',
}),
urlMiddleware({
url: 'http://localhost:3000/graphql',
}),
]));
Note: This enables logging and by default it's a simple console.log.
Here is how I threw the error:
const params = {
email: email.toLowerCase(),
businessDisplayName: business,
business: business.toLowerCase()
}
var onSuccess = () => {
console.log('Mutation successful!')
}
var onFailure = transaction => {
var error = transaction.getError() || new Error('Mutation failed.')
console.error(error)
}
Relay.Store.commitUpdate(new FindOrCreateUser({ user: { ...params } }), { onFailure, onSuccess })
And of course you always need to clean your cache and restart your packager.