router.patch is returning 404 "not found" - node.js

I am working on small node api and I have an issue with patch method.
My router.patch is returning me 404.
This is how my route looks:
router.param('userId', findById);
router.patch(
'/api/projects/update/:projectId/:userId',
authCheck,
isAdmin,
findProjectById,
update
);
The findById is based on my :userId param. Whole method looks like this:
exports.findById = async (req, res, next) => {
try {
let user = await User.findById(req.params.userId);
if (!user) return res.status(400).json({ msg: 'User not found' });
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'User not found' });
}
res.status(500).send('Server Error');
}
};
Based on that I should get proper user for proper project.
My two ayhorization methods:
exports.authCheck = async (req, res, next) => {
try {
/* get token from header
replace('Bearer', '') - this will remove bearer from token header
*/
const token = req.header('Authorization').replace('Bearer', '');
//check if no token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
/*
decoded contains _id as a payload in token. Id is from getAuthToken */
const decoded = jwt.verify(token, config.get('jwtSecret'));
const user = await User.findOne({
_id: decoded._id,
'tokens.token': token,
});
if (!user) {
throw new Error();
}
req.token = token;
req.user = user;
next();
} catch (err) {
res.status(401).json({ msg: 'Please authenticate' });
}
};
exports.isAdmin = async (req, res, next) => {
try {
if (req.user.role !== config.get('roleSecret')) {
return res.status(403).json({
errors: [
{
msg: 'No Admin rights. Access Denied!!',
},
],
});
}
next();
} catch (err) {
res.status(403).json({ msg: 'Forbidden access' });
}
};
Finaly, my project controller where i have findProjectById, update
In findProjectById I am looking for project based on route param and i assing it to project
exports.findProjectById = async (req, res, next) => {
const _id = req.params.projectId;
try {
let project = await Project.findById(_id);
if (!project) return res.status(400).json({ msg: 'Porject not found' });
req.project = project;
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'Porject not found' });
}
res.status(500).send('Server Error');
}
};
My update method i s not done, because i was testing if anything heppens
exports.update = async (req, res) => {
try {
const proj = await req.project;
const _id = proj._id;
await Project.findByIdAndUpdate(_id, req.body, {
new: true,
runValidators: true,
});
if (!proj) {
return res.status(404).json({ msg: 'Project not found' });
}
return res.json(proj);
} catch (err) {
res.status(500).send('Server Error');
}
};
Not sure what am I missing here, but after few hours and lot of searching still can't get this working

Get this working. Issue was in my router path.
/api/projects/update/:projectId/:userId
Should be
/projects/update/:projectId/:userId
this can be closed

Related

can't set status of 400 in express

I have a simple web service and it has a route for register user ,
I want when email exists in DB throw an error with status of 400 or other
I've done it like this
controllers/user.js
const { User } = require('../models/user')
exports.create = async (req, res) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
return res.json({ err: 'email already exists' })
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
res.status(400).send({ err })
}
}
BUT , it always give status of 200,
where is the problem ?
Add the status to your response:
if (user) {
return res.status(400).json({ err: 'email already exists' })
}
You can simply send the status 400 when checking if(user)
if(user){
res.status(400).jsom({ err: "Email already exists" });
}
OR
Threat the errors and add a middleware using next (a little bit more complicated then the first one, but more proffessional)
exports.create = async (req, res, next) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
throw new Error("Email already exists");
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
next(err, req, res, next);
}
}
In the next middleware you can threat the error and send whatever response you need. (err, req, res objects are sent like references, so you can use them there)

Login Authentification: No response from Rest API after Post Request

I recently switched from php development to Javascript (I'm really amazed by the performance and possibilities).
Currently I try to create a simple authentification function (Username,hashed Password checked to mariadb Database)
After following some tutorials I managed to create the following structure:
But when I try to test the API via Postman and Insomnia I just get no response. Not even an Error Code. Just going on forever, just like an infinite Loop?
I'm thankful for any tip as I'm new to this. Thanks in advance.
My Stack: React, Nodejs, Mariadb, Express & Jwt / bcryptjs
My Express Router router.js:
router.post('/login', (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE username = ${pool.escape(req.body.username)};`,
(err, result) => {
// user does not exists
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
if (!result.length) {
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
// check password
bcrypt.compare(
req.body.password,
result[0]['password'],
(bErr, bResult) => {
// wrong password
if (bErr) {
throw bErr;
}
if (bResult) {
const token = jwt.sign({
username: result[0].username,
userId: result[0].id
},
process.env.API_SecretKey, {
expiresIn: '2h'
}
);
return res.status(200).send({
msg: 'Logged in!',
token,
user: result[0]
});
}
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
);
}
);
});
router.post('/sign-up', userMiddleware.validateRegister, (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE LOWER(username) = LOWER(${pool.escape(
req.body.username
)});`,
(err, result) => {
if (result.length) {
return res.status(409).send({
msg: 'This username is already in use!'
});
} else {
// username is available
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).send({
msg: err
});
} else {
// has hashed pw => add to database
pool.query(
`INSERT INTO TABLE (SecurityID, userPassword, username, userOTP) VALUES ('${pool.escape}', ${pool.escape(
req.body.SecurityID,
req.body.username,
req.body.password,
req.body.userOTP
)}, ${pool.escape(hash)}, now())`,
(err, result) => {
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
return res.status(201).send({
msg: 'Registered!'
});
}
);
}
});
}
}
);
pool.end;
});
router.get('/secret-route', userMiddleware.isLoggedIn, (req, res, next) => {
console.log(req.userData);
res.send('This is the secret content. Only logged in users can see that!');
});
module.exports = router;
My Middleware users.js
module.exports = {
validateRegister: (req, res, next) => {
// username min length 3
if (!req.body.username || req.body.username.length < 3) {
return res.status(400).send({
msg: 'Passwort:' + req.body.username + 'Please enter a username with at least 3 chars',
});
}
// password min 6 chars
if (!req.body.password || req.body.password.length < 6) {
return res.status(400).send({
msg: 'Passwort:' + req.body.password + 'Please enter a password with at least 6 chars'
});
}
// password (repeat) does not match
if (
!req.body.password_repeat ||
req.body.password != req.body.password_repeat
) {
return res.status(400).send({
msg: 'Both passwords must match'
});
}
next();
},
isLoggedIn: (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(
token,
process.env.API_SecretKey
);
req.userData = decoded;
next();
} catch (err) {
return res.status(401).send({
msg: 'Your session is not valid!'
});
}
}
};
My index.js:
const express = require("express");
const DigitalMangement = express();
const cors = require('cors');
require("dotenv").config();
DigitalMangement.use(cors());
DigitalMangement.use(express.json());
// add routes
const router = require('./Routes/router.js');
DigitalMangement.use("/api", router);
DigitalMangement.listen(process.env.Application_Port, () => {
console.log("Server is running on Port " + process.env.Application_Port)
});
I haven't reviewed the whole code but, if you throw the error the code block will not continue. In this case, it won't be logged or sent as a response. Try removing the throw err line and rerun the code.
if (err) {
throw err; //! here
return res.status(400).send({
msg: err
});
}
Thanks for all the help fellow Coders:
It seems to be that the import MariaDB isn't 100% correct in this situation.
I changed it to mariadb/callback and it started to work.
The MariaDB library returns Promises and mariadb/callback allows callbacks.

Chaining 2 auth tokens in one endpoint

I'm trying to chain 2 auth tokens in one endpoint, the user and the admin, to get all merchants.
I have tried several solutions posted by several people here on stackoverflow, with no luck. I'll post my code along with the solutions I tried below.
How can I make express to continue the process if the request has ONE of the 2 tokens, admin OR user?
Thanks in advance...
Admin auth:
const adminAuth = async (req, res, next) => {
try {
const token = req.header('Authorization');
if(!token) {
return res.status(401).json({ message: 'Unauthorized', success: false });
}
const newToken = req.header('Authorization').split(' ')[1];
const decoded = JWT.verify(newToken, key);
if(decoded.role != 'admin') {
return res.status(401).json(
{ message: 'Unauthorized, only an admin is authorized!', success: false }
);
}
const findAdmin = await Admin.findOne({ username: decoded.username });
if(!findAdmin){
return res.status(409).send({ message: `Admin doesn't exist!`, success: false });
}
} catch (err) {
console.log(err);
}
next();
}
User auth:
const userAuth = async (req, res, next) => {
try {
const token = req.header('Authorization');
if(!token) {
return res.status(401).json({ message: 'Unauthorized', success: false });
}
const newToken = req.header('Authorization').split(' ')[1];
const decoded = JWT.verify(newToken, key);
if(decoded.role != 'user') {
return res.status(401).json({ message: 'Unauthorized, only a user is authorized!', success: false });
}
const findUser = await User.findOne({ email: decoded.email });
if(!findUser){
return res.status(409).send({ message: `User doesn't exist!`, success: false });
}
} catch (err) {
console.log(err);
}
next();
}
The endpoint in question:
router.get('/getall', adminAuth, usertAuth, upload.none(), async (req, res) => {
const merchantsList = await Merchant.find();
if(!merchantsList) {
return res.status(500).send({ success: false });
}
return res.status(200).send({ merchants: merchantsList, success: true });
});
I tried the below solutions:
router.get('/getall', [adminAuth, usertAuth], upload.none(), async (req, res) => {
const merchantsList = await Merchant.find();
if(!merchantsList) {
return res.status(500).send({ success: false });
}
return res.status(200).send({ merchants: merchantsList, success: true });
});
router.get('/getall', [adminAuth || usertAuth], upload.none(), async (req, res) => {
const merchantsList = await Merchant.find();
if(!merchantsList) {
return res.status(500).send({ success: false });
}
return res.status(200).send({ merchants: merchantsList, success: true });
});
router.get('/getall', [adminAuth, usertAuth, upload.none(), async (req, res) => {
const merchantsList = await Merchant.find();
if(!merchantsList) {
return res.status(500).send({ success: false });
}
return res.status(200).send({ merchants: merchantsList, success: true });
}]);
router.get('/getall', [adminAuth, usertAuth, upload.none()], async (req, res) => {
const merchantsList = await Merchant.find();
if(!merchantsList) {
return res.status(500).send({ success: false });
}
return res.status(200).send({ merchants: merchantsList, success: true });
});
That's not how middlewares work in express for example let's look at getAll route
router.get('/getall', [adminAuth, usertAuth], upload.none(), async (req, res) => {
try{
//some code
}catch(e){
//handle error
}
});
if the user is not admin it will fail at adminAuth and end the request, so you have to think of middlewares as a chain or a pipleline they pass (req,res,next) to each other
so what you need here is not make a third middleware which is the combination of adminAuth and userAuth
async function adminAndUserAuth (req, res, next){
try {
const token = req.header('Authorization');
if(!token) {
return res.status(401).json({ message: 'Unauthorized', success: false });
}
const newToken = req.header('Authorization').split(' ')[1];
const decoded = JWT.verify(newToken, key);
if(decoded.role != 'admin' && decoded.role != 'user' ) {
return res.status(401).json(
{ message: 'Unauthorized, only an admin or user are authorized!', success: false }
);
}
const found = await Admin.findOne({ username: decoded.username }) ?? await User.findOne({ email: decoded.email });
if(!found){
return res.status(409).send({ message: `Admin doesn't exist!`, success: false });
}
} catch (err) {
console.log(err);
}
next();
}
and the new route will be
router.get('/getall' , adminAndUserAuth , async (req,res) => {});

Next.js's API route does not send response

Next.js sends this error when requesting my API route:
API resolved without sending a response for /api/login, this may result in stalled requests.
The content of the API route is, I guess, valid. Most edgecases was solved. I'll also add that the error was occuring when successfully logged.
export default withSession(async (req, res) => {
if (req.method !== "POST") {
return res.status(405).send({ error: "Tylko metoda POST jest dozwolona." });
}
const { username, password } = req.body;
if (!username || !password) {
return res.status(401).send({ error: "Nazwa użytkownika i hasło nie mogą być puste." });
}
try {
const knex = getKnex();
const user = await knex<User>("users").select("*").where("username", username).first();
if (!user) {
return res.status(401).send({ error: "Użytkownik o takiej nazwie nie istnieje." });
}
bcrypt.compare(password, user.password, async function (error) {
if (error) {
return res.status(403).send({ error: "Podane hasło jest nieprawidłowe." });
}
const { password, ...result } = user;
req.session.set("user", result);
await req.session.save();
res.status(200).send({ message: "Zostałeś zalogowany." });
});
} catch (error) {
res.status(error?.status || 500).send({ error: error.message });
console.error(error.stack);
}
});
The withSession function is a utility for handling next-iron-session.
Attempt to add a return before calling the bcrypt and on its final response, such as:
return bcrypt.compare(password, user.password, async function (error) {
if (error) {
return res.status(403).send({ error: "Podane hasło jest nieprawidłowe." });
}
const { password, ...result } = user;
req.session.set("user", result);
await req.session.save();
return res.status(200).send({ message: "Zostałeś zalogowany." });
});
You can delete this warning by exporting a config object to change the default configs.
it tells the server that this route is being handled by an external resolver
export const config = {
api: {
externalResolver: true,
},
}
reference

Not getting correct status code (409) if email exists using Next.js, Mongoose, MongoDb Atlas and Express

I am building a login/Register portion of my app. Right now I'm using express-validator to check if an email exists in my collection.
This is my route:
var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body } = require('express-validator');
router
.route('/registration')
.get(function(req, res) {
console.log(0)
UserModel.find({}, (err, users) => {
console.log(1);
if (err) return res.status(500).send(err)
console.log(2);
return res.json(users);
})
})
.post(body('username_email').custom(value => {
console.log("value ", value);
console.log(3)
UserModel.findOne({ 'username_email': value }, (err) => {
console.log(4);
if (err) return res.status(409).send(err);
})
}), async(req, res, next) => {
console.log(5)
try {
let newUser = new UserModel(req.body);
let savedUser = await newUser.save();
console.log(6);
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router
In my component on the front end I have a function in my fetch which will catch the error if there is one.
handleErrors(response) {
if (!response.ok) {
console.log('This email exists!')
throw Error(response.statusText);
}
return response;
}
handleSubmit(event) {
event.preventDefault()
var { username, password } = this.state
var mailFormat = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
var error = false
if (!username.match(mailFormat)) {
this.setState({ usernameError: true })
error = true
} else {
this.setState({ usernameError: false })
}
if (password.length <= 8) {
this.setState({ passwordError: true })
error = true
} else {
this.setState({ passwordError: false })
}
console.log(`error ${error}`)
if (error == false) {
this.setState({ formError: false, formSuccess: true })
}
window.fetch('http://localhost:8016/users/registration', {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ username_email: username, password: password })
})
.then(this.handleErrors)
.then(function (response) {
console.log(`response ${response}`)
return response.json()
}).then(function (data) {
console.log('User created:', data)
}).catch(function (error) {
console.log(error);
});
}
The console.log in the fetch, handleErrors is being registered in the console, but why isn't the error status a 409 like I indicated.
Closer excerpt of post route!
.post(body('username_email').custom(value => {
console.log("value ", value);
console.log(3)
Is this the problem? Node style should have a error and callback?
UserModel.findOne({ 'username_email': value }, (err) => {
console.log(4);
if (err) return res.status(409).send(err);
})
}), async(req, res, next) => {
console.log(5)
try {
let newUser = new UserModel(req.body);
let savedUser = await newUser.save();
console.log(6);
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
UPDATE
I tried Nick's solution but I get this:
MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }
at Function.create (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/error.js:43:12)
at toError (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/utils.js:149:22)
at coll.s.topology.insert (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/operations/collection_ops.js:859:39)
at handler (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/topologies/replset.js:1155:22)
at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/connection/pool.js:397:18
at process._tickCallback (internal/process/next_tick.js:61:11)
POST /users/registration 500 312.485 ms - 51
^C
Two things I am noticing:
I get back MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }
which is the error from having a duplicate email, but number one where is E-mail already in use message in the console from the promise? And two how can I pass the error status "res.status(409).send(err);" from the promise?
The issue was that during your validation, you weren't returning the promise since the mongoose call is async. The rest the code ran before your validator was finished. I commented where you were missing the return.
router.route('/registration')
.get(function(req, res) {
UserModel.find({}, (err, users) => {
if (err) res.status(500).send(err)
res.json(users)
})
})
.post(body('username').custom(value => {
return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), async(req, res, next) => {
try {
let newUser = new UserModel(req.body)
let savedUser = await newUser.save(err => {
if (err) return res.json({ success: false, error: err })
return res.json({ success: true })
})
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router
UPDATE
Just read through express-validator docs. I think you would need to validate the errors during the request process
var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body, validationResult } = require('express-validator');
router.route('/registration')
.get(function(req, res) {
UserModel.find({}, (err, users) => {
if (err) res.status(500).send(err)
res.json(users)
})
})
.post(body('username').custom(value => {
return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), async(req, res, next) => {
// Checks for errors in validation
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
try {
let newUser = new UserModel(req.body)
let savedUser = await newUser.save(err => {
if (err) return res.json({ success: false, error: err })
return res.json({ success: true })
})
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router

Resources