Related
Not sure what's going on but every time I try to send an email i'm met with a error message. I've literally tried everything I can think of and Nothing. This is the message i'm receiving from postman when I hit the localhost:4000/api/send-mail route. I've been trying to work through this for hours now, if anyone can help that would be amazing. Thanks in advance!!!
{
"message": "Unauthorized",
"code": 401,
"response": {
"headers": {
"server": "nginx",
"date": "Mon, 21 Mar 2022 01:57:04 GMT",
"content-type": "application/json",
"content-length": "116",
"connection": "close",
"access-control-allow-origin": "https://sendgrid.api-docs.io",
"access-control-allow-methods": "POST",
"access-control-allow-headers": "Authorization, Content-Type, On-behalf-of, x-sg-elas-acl",
"access-control-max-age": "600",
"x-no-cors-reason": "https://sendgrid.com/docs/Classroom/Basics/API/cors.html",
"strict-transport-security": "max-age=600; includeSubDomains"
},
"body": {
"errors": [
{
"message": "The provided authorization grant is invalid, expired, or revoked",
"field": null,
"help": null
}
]
}
}
}
my user controllers code
const expressAsyncHandler = require("express-async-handler");
const sgMail = require("#sendgrid/mail");
const generateToken = require("../../config/token/generateToken");
const User = require("../../model/user/User");
const validateMongodbId = require("../../utils/validateMongodbID");
sgMail.setApiKey(process.env.SEND_GRID_API_KEY);
//-------------------------------------
//Register
//-------------------------------------
const userRegisterCtrl = expressAsyncHandler(async (req, res) => {
//Check if user Exist
const userExists = await User.findOne({ email: req?.body?.email });
if (userExists) throw new Error("User already exists");
try {
//Register user
const user = await User.create({
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
password: req?.body?.password,
});
res.json(user);
} catch (error) {
res.json(error);
}
});
//-------------------------------
//Login user
//-------------------------------
const loginUserCtrl = expressAsyncHandler(async (req, res) => {
const { email, password } = req.body;
//check if user exists
const userFound = await User.findOne({ email });
//Check if password is match
if (userFound && (await userFound.isPasswordMatched(password))) {
res.json({
_id: userFound?._id,
firstName: userFound?.firstName,
lastName: userFound?.lastName,
email: userFound?.email,
profilePhoto: userFound?.profilePhoto,
isAdmin: userFound?.isAdmin,
token: generateToken(userFound?._id),
});
} else {
res.status(401);
throw new Error("Invalid Login Credentials");
}
});
//------------------------------
//Users
//-------------------------------
const fetchUsersCtrl = expressAsyncHandler(async (req, res) => {
console.log(req.headers);
try {
const users = await User.find({});
res.json(users);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Delete user
//------------------------------
const deleteUsersCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const deletedUser = await User.findByIdAndDelete(id);
res.json(deletedUser);
} catch (error) {
res.json(error);
}
});
//----------------
//user details
//----------------
const fetchUserDetailsCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const user = await User.findById(id);
res.json(user);
} catch (error) {
res.json(error);
}
});
//------------------------------
//User profile
//------------------------------
const userProfileCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
try {
const myProfile = await User.findById(id);
res.json(myProfile);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Update profile
//------------------------------
const updateUserCtrl = expressAsyncHandler(async (req, res) => {
const { _id } = req?.user;
validateMongodbId(_id);
const user = await User.findByIdAndUpdate(
_id,
{
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
bio: req?.body?.bio,
},
{
new: true,
runValidators: true,
}
);
res.json(user);
});
//------------------------------
//Update password
//------------------------------
const updateUserPasswordCtrl = expressAsyncHandler(async (req, res) => {
//destructure the login user
const { _id } = req.user;
const { password } = req.body;
validateMongodbId(_id);
//Find the user by _id
const user = await User.findById(_id);
if (password) {
user.password = password;
const updatedUser = await user.save();
res.json(updatedUser);
} else {
res.json(user);
}
});
//------------------------------
//following
//------------------------------
const followingUserCtrl = expressAsyncHandler(async (req, res) => {
//1.Find the user you want to follow and update it's followers field
//2. Update the login user following field
const { followId } = req.body;
const loginUserId = req.user.id;
//find the target user and check if the login Id exists
const targetUser = await User.findById(followId);
const alreadyFollowing = targetUser?.followers?.find(
(user) => user?.toString() === loginUserId.toString()
);
if (alreadyFollowing) throw new Error("You are already folliwig this user");
//1. Find the user you want to follow and update it's followers field
await User.findByIdAndUpdate(
followId,
{
$push: { followers: loginUserId },
},
{ new: true }
);
//2. Update the login user following field
await User.findByIdAndUpdate(
loginUserId,
{
$push: { following: followId },
isFollowing: true,
},
{ new: true }
);
res.json("You are now following this user");
});
//------------------------------
//unfollow
//------------------------------
const unfollowUserCtrl = expressAsyncHandler(async (req, res) => {
const { unFollowId } = req.body;
const loginUserId = req.user.id;
await User.findByIdAndUpdate(
unFollowId,
{
$pull: { followers: loginUserId },
isFollowing: false,
},
{ new: true }
);
await User.findByIdAndUpdate(
loginUserId,
{
$pull: { following: unFollowId },
},
{ new: true }
);
res.json("You have successfully unfollowed this user");
});
//------------------------------
//Block Users
//------------------------------
const blockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: true,
},
{ new: true }
);
res.json(user);
});
//------------------------------
//Block user
//------------------------------
const unBlockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: false,
},
{ new: true }
);
res.json(user);
});
//------------------------------
// Account Verification - Send email
//------------------------------
const generateVerificationTokenCtrl = expressAsyncHandler(async (req, res) => {
try {
//build your message
const msg = {
to: "MizTamaraAndrea#gmail.com",
from: "Tamara18_1985#msn.com",
subject: "My first Node js email sending",
text: "I hope this goes through",
};
await sgMail.send(msg);
res.json("Email sent");
} catch (error) {
res.json(error);
}
});
module.exports = {
generateVerificationTokenCtrl,
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
};
My user Routes Code
const express = require("express");
const {
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
generateVerificationTokenCtrl,
} = require("../../controllers/users/usersCtrl");
const authMiddleware = require("../../middleware/auth/authMiddleware");
const userRoutes = express.Router();
userRoutes.post("/register", userRegisterCtrl);
userRoutes.post("/login", loginUserCtrl);
userRoutes.post("/send-mail", generateVerificationTokenCtrl);
userRoutes.get("/", authMiddleware, fetchUsersCtrl);
userRoutes.put("/password", authMiddleware, updateUserPasswordCtrl);
userRoutes.put("/follow", authMiddleware, followingUserCtrl);
userRoutes.put("/unfollow", authMiddleware, unfollowUserCtrl);
userRoutes.put("/block-user/:id", authMiddleware, blockUserCtrl);
userRoutes.put("/unblock-user/:id", authMiddleware, unBlockUserCtrl);
userRoutes.get("/profile/:id", authMiddleware, userProfileCtrl);
userRoutes.put("/:id", authMiddleware, updateUserCtrl);
userRoutes.delete("/:id", deleteUsersCtrl);
userRoutes.get("/:id", fetchUserDetailsCtrl);
module.exports = userRoutes;
You have one of two problems here. Either, your API key does not have the permission to send messages, or has been deleted. Create yourself a new API key that has permission to send emails and try again.
Or, I see you appear to be trying to send from an msn.com email address. In order to send from a third party domain like that, you need to have set up single sender verification. This lets SendGrid know that you do have access to that email address and that you are not trying to spoof someone else's.
(Note that when you ship to production, we recommend you use your own domain and authenticate that domain with SendGrid, which will give you much better deliverability.)
I have a function to check if a user exists, and a function to create a new user in my User model.
What I want to do is call them in the router to check if a user with the email adress in req.body already exists.
If it does, I want to return a message, and if not, I want to create the user.
When I try to call the route in Postman, I get this error in node console :
node_modules/express/lib/response.js:257
var escape = app.get('json escape')
TypeError: Cannot read properties of undefined (reading 'get')
User model :
const Sequelize = require("sequelize");
const connexion = require("../database");
const User = connexion.define(
"users",
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
email: {
type: Sequelize.STRING(100),
allowNull: false,
},
password: {
type: Sequelize.STRING(100),
allowNull: false,
},
},
{
freezeTableName: true
}
);
function checkUser(userEmail) {
const findUser = User.findOne({ where: { userEmail } }).catch((err) => {
console.log(err);
});
if (findUser) {
return res.json({ message: "Cette adresse email est déjà enregistrée" });
} else {
return false;
}
}
function createUser(userData) {
console.log(userData);
User.create(userData)
.then((user) => {
console.log(user);
})
.catch((err) => {
console.log(err);
});
}
module.exports = { createUser, checkUser };
user controller :
const createUser = require("../models/User");
const bcrypt = require("bcrypt");
const saltRounds = 10;
addUser = async (req, res) => {
try {
const userData = req.body;
console.log(req.body);
bcrypt.hash(userData.password, saltRounds, async function (err, hash) {
userData.password = hash;
const newUser = await createUser(req.body);
res.status(201).json({ newUser });
});
} catch (err) {
console.log(err);
res.status(500).json("Server error");
}
};
module.exports = addUser;
user router :
const express = require("express");
const router = express.Router();
const addUser = require("../controllers/userController");
const { checkUser } = require("../models/User");
router.post("/", async (req, res) => {
const { email } = req.body;
const alreadyExists = await checkUser(email);
if (!alreadyExists) {
addUser(req.body);
}
});
module.exports = router;
EDIT : Finally I'm trying a more simple way. I will do the check part directly into the createUser function.
But now, it creates the user even if the email already exists ^^
async function createUser(userData) {
console.log(userData);
const findUser = await User.findOne({ where: userData.email }).catch(
(err) => {
console.log(err);
}
);
findUser
? console.log(findUser)
: User.create(userData)
.then((user) => {
console.log(user);
})
.catch((err) => {
console.log(err);
});
}
i think the problem is with this part you are trying to use res but it doesn't exist in your checkUser function
if (findUser) {
return res.json({ message: "Cette adresse email est déjà enregistrée" });
} else {
return false;
}
try this instead
if (findUser) {
return true });
} else {
return false;
}
UPDATE to fix the problem of user creation if it already exists
async function createUser(userData) {
console.log(userData);
const findUser = await User.findOne({ where: userData.email }).catch(
(err) => {
console.log(err);
}
);
if(!findUser){
findUser
? console.log(findUser)
: User.create(userData)
.then((user) => {
console.log(user);
})
.catch((err) => {
console.log(err);
});
}
}
Problem solved by doing this (thanks super sub for your help):
async function createUser(userData) {
console.log(userData);
const email = userData.email;
const findUser = await User.findOne({ where: { email } }).catch((err) => {
console.log(err);
});
if (!findUser) {
User.create(userData)
.then((user) => {
console.log(user);
})
.catch((err) => {
console.log(err);
});
}
}
i have an express app with a put request that updates a phonebook if i found that the person's name already exists in this phone book (i'm using the "mongoose-unique-validator" that have the unique: true option in validation)
but i have the problem with this put request only when i set findByIdAndUpdate's runValidators to true
here is my code
the schema
const personSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 3, unique: true },
number: {
type: String,
required: true,
validate: {
validator: function (str) {
//the function to validate the number
},
message: "phone number must contain at least 8 digits",
},
},
});
personSchema.plugin(uniqueValidator);
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
const Person = mongoose.model("Person", personSchema);
the put request
app.put("/api/persons/:id", (req, res, next) => {
const id = req.params.id;
console.log(id);
const body = req.body;
const person = {
name: body.name,
number: body.number,
};
// opts is supposed to be true; and this is where i have the problem
const opts = { runValidators: false };
Person.findByIdAndUpdate(id, person, opts)
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((error) => next(error));
});
the error handler
const errorHandler = (error, req, res, next) => {
console.error(error.message);
if (error.name === "CastError" && error.kind == "ObjectId") {
return res.status(400).send({ error: "malformatted id" });
} else if (error.name === "ValidationError") {
return res.status(400).json({ error: error.message });
}
next(error);
};
app.use(errorHandler);
the error i'm getting is
error: "Validation failed: name: Cannot read property 'ownerDocument' of null"
#oussama ghrib If you're not updating the name and you're only updating the number (as you indicated), the update object can just have the phone number, like below:
app.put('/api/persons/:id', (req, res, next) => {
const { number } = req.body
Person.findByIdAndUpdate(req.params.id, { number }, {
new: true,
runValidators: true,
context: 'query'
})
.then((updatedPeep) => {
res.json(updatedPeep)
})
.catch((err) => next(err))
})
If you're using runValidators: true you also need context: 'query' as explained here: https://github.com/blakehaswell/mongoose-unique-validator#find--updates
findOneAndUpdate works like findByIdAndUpdate for this purpose.
The issue is with your usage of: findByIdAndUpdate(id, update, opts)
The update object should be properties you want to update otherwise it will also to try update the id.
Solution:
app.put("/api/persons/:id", (req, res, next) => {
const { name, number } = req.body;
Person.findByIdAndUpdate(req.params.id, { name, number }, opts )
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((err) => next(err));
}
https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate
I have been attempting to use Express Validator, however I have come across a problem.
Currently, I am using one route with a variable (which is the action, e.g update name, email, password etc.) to send the updated user data to the server.
I am using a switch statement that looks at the action, and does the relevant update to the user data.
I'd like one validation function that also has a switch statement, which will determine what it needs to validate.
Currently I have this so far...
this validation rules:
const validationRules = () => {
console.log("validating...");
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
};
the post request:
app.post(
"/api/user/update/:action",
validationRules(),
validate,
(req, res) => {
const { id } = req.body;
const { action } = req.params;
const updateDatabase = (id, updateObject) => {
UserModel.findOneAndUpdate(
{ _id: id },
{ $set: updateObject },
{
useFindAndModify: false,
}
)
.then((user) => {
console.log(user);
res.sendStatus(200);
})
.catch((err) => {
console.log(err);
res.status(403).send({ error: err });
});
};
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
let update = {};
switch (action) {
case "name":
const { firstName, lastName } = req.body;
update = {
"userInfo.firstName": firstName,
"userInfo.lastName": lastName,
};
break;
case "email":
const { email } = req.body;
console.log(email);
update = {
"userInfo.email": email,
};
break;
case "mobile":
const { mobile } = req.body;
update = {
"userInfo.mobile": mobile,
};
break;
default:
console.log("nothing matched so nothing updated");
break;
}
updateDatabase(id, update);
}
}
);
You can make a simple middlware and the switch case there:
Here is an example:
const app = require('express')();
const validatorMiddleware = (req, res, next)=>{
switch(req.params.action){
case 'foo':
//here you will `return` the rules
console.log('foo');
break;
case 'bar':
//here you will `return` the rules
console.log('bar');
break;
default: console.log("default");
}
next();
}
app.get('/:action', validatorMiddleware, (req, res)=>{
res.send(req.params);
})
app.listen(8080, ()=>{
console.log('started');
})
For your case it will be something like this:
const validationRules = (req, res, next) => {
console.log("validating...");
switch (req.params.action) {
case "name":
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
}
next();
};
I am creating web API using mongoose.
POST and GET work, but I have no idea how to implement PUT method in mongoose.
Here is what I created:
board.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config = require('../config/database');
const BoardSchema = mongoose.Schema({
_id: {
type: String
},
position: {
type: [String]
}
});
const Board = module.exports = mongoose.model('boards', BoardSchema);
module.exports.getBoardById = function (id, callback)
{
Board.findById(id, callback);
}
module.exports.addBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
module.exports.updateBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
users.js
router.put('/board/:id', (req, res, next) =>
{
let newBoard = new Board({
_id: req.params.id,
position: req.body.position
});
Board.updateBoard(newBoard, (err, board) =>
{
if (err)
{
res.json({ newBoard: newBoard, success: false, msg: "Failed to update board" });
}
else
{
res.json({ newBoard: newBoard, success: true, msg: "Board added" });
}
});
});;
Here, in the board.js, I created methods for adding a new board and updating to existing board. .addBoard is working correctly and am able to test it using Postman. But, .updateBoard adds the data when the data does not exist, but does not update any data and returns false as response (just like POST does). Is there any way I can make the PUT method works?
Thank you!
Please let me know if this works for you! I want to introduce you to http://mongoosejs.com/docs/api.html#findbyidandupdate_findByIdAndUpdate
router.put('/board/:id', (req, res) => {
const {id: _id} = req.params // Assigning id to _id which is a es6 feature. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const {position} = req.body
const newBoard = {
_id,
position
}
Board.findByIdAndUpdate(
_id,
newBoard,
(err, updatedBoard) => {
if (err) {
res.json({
newBoard,
success: false,
msg: 'Failed to update board'
})
} else {
res.json({newBoard, success: true, msg: 'Board added'})
}
}
)
})
why are you using save method while updating?
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config = require('../config/database');
const BoardSchema = mongoose.Schema({
_id: {
type: String
},
position: {
type: [String]
}
});
const Board = module.exports = mongoose.model('boards', BoardSchema);
module.exports.getBoardById = function (id, callback)
{
Board.findById(id, callback);
}
module.exports.addBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
module.exports.updateBoard = function (condition, update, callback)
{
Board.update(condition,update,callback);
}
in controller
router.put('/board/:id', (req, res, next) =>
{
let newBoard = new Board({
_id: req.params.id,
position: req.body.position
});
Board.updateBoard({ _id: req.params.id } ,newBoard, (err, board) =>
{
if (err)
{
res.json({ newBoard: newBoard, success: false, msg: "Failed to update board" });
}
else
{
res.json({ newBoard: newBoard, success: true, msg: "Board added" });
}
});
});
try this.
As you are using req.body i think you are trying to call a put request from a form (sometimes happens with AJAX requests also). For doing that use method-overide. And set the xhr header as given in the documentation. This will surely work.