How to fix router.delete() which is not working - Express.js? - javascript

I am trying to run a delete request but it is not working, I have used the exact same logic on another project exactly like it and it works there.
Here is the route file which includes the delete request as well as the post request that does indeed work
const express = require("express");
const router = express.Router();
const User = require("../models/users");
const cardSchema = require("../models/card");
//add card request
router.post("/:id/addcard", getUser, async (req, res) => {
try {
if (req.body != null) {
const newCard = new cardSchema({
name: req.body.name,
cardNumber: req.body.cardNumber,
ccv: req.body.ccv,
expiration: req.body.expiration,
});
res.user.cardInfo.push(newCard);
}
const updatedCardInfo = await res.user.save();
return res.status(200).json(updatedCardInfo);
} catch (error) {
return res.status(400).json({ message: error.message });
}
});
//delete card request
router.delete("/:id/deletecard", getUser, async (req, res) => {
if (req.body !== null) {
res.user.cardInfo.remove(req.body);
}
try {
const updatedUser = await res.user.save();
res.status(200).json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
//get user middleware
async function getUser(req, res, next) {
let user;
try {
user = await User.findById(req.params.id);
if (user == null) {
return res.status(404).json({ message: "Cannot find user" });
}
} catch (error) {
return res.status(500).json({ message: error.message });
}
res.user = user;
next();
}
module.exports = router;
I have triple checked that I am using the correct URL and passing in the correct information in the req.body. I recieved the users information after calling the delete request but just does not remove the card information. I have also checked in my database that it is 'cardInfo' so there is no spelling mistake there either.

Related

User is not authenticated jswtoken

I have created a login page and a about page the user will only access the about page if the user is logged in.
I am trying to authenticate the user by using the tokens generated while signing in, but the token is not getting authenticated even after signing in with the correct credentials. I don't know what is the problem?
This is code to my sign-in and token generating method
const express = require("express");
const { default: mongoose } = require("mongoose");
const router = express.Router();
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
require("../db/conn");
const User = require("../model/userSchema");
const cookieParser = require('cookie-parser');
const Authenticate = require("../middleware/authenticate");
router.use(cookieParser());
//LOgin route
router.post("/signin", (req, res)=>{
if(!req.body.email || !req.body.password){
return res.status(400).json({error: "Plz fill the required data"});
}else{
bcrypt.hash(req.body.password, 12, function (err, hash) {
User.findOne({email: req.body.email}, function (err, foundUser) {
if(err){
console.log(err);
}else{
if(foundUser){
bcrypt.compare(req.body.password, foundUser.password, function (err, result) {
if(result){
return res.json({message: "successfully log in"})
}else{
return res.json({message: "incorrect password"});
}
});
const email = req.body.email;
const token = jwt.sign(
{ user_id: foundUser._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "720h",
}
);
foundUser.tokens = foundUser.tokens.concat({token: token});
foundUser.save();
// res.status(200).json(foundUser);
console.log(foundUser);
}else{
return res.status(400).json({message: "user not found"});
};
}
})
})
}
});
//about us page
router.get("/about", Authenticate, function (req, res) {
console.log("about running");
res.send(req.rootUser);
});
module.exports = router;
this is the code to authenticate the user
require("dotenv").config({path: "./config.env"});
const jwt = require("jsonwebtoken");
const User = require("../model/userSchema");
const Authenticate = async(req, res, next) =>{
try {
const token = req.cookies.jwtoken;
const verifyToken = jwt.verify(token, process.env.TOKEN_KEY);
const rootUser = await User.findOne({ _id: verifyToken._id, "tokens.token": token});
if(!rootUser) {
throw new Error("User not found")
}
req.token = token;
req.rootUser = rootUser;
req.userID = rootUser._id;
next();
} catch (err) {
console.log(err);
return res.status(401).send("Unauthorized: No token provided");
}
}
module.exports = Authenticate;
This is react based code of: About-page to display it or not based on user's authenticity.
const navigate = useNavigate();
const callAboutPage = async() =>{
try {
const res = await fetch("/about",{
method: "GET",
headers: {
Accept: "application/json",
"Content-Type" : "application/json"
},
credentials: "include"
});
const data = await res.json();
console.log(data);
if(!res.status === 200){
const error = new Error(res.error);
throw error;
}
} catch (err) {
console.log(err);
navigate("/login");
}
}
As said in the comment looks like there is a issue on the process for setting up the jwtoken, and when you sign in, you just need to find the user and compare the password, there is no need to do the hash with Bcrypt, since you're not registing new user, for example, i will use Async/await instead of callback function, in order for you to read it much more easier:
//Login route
router.post("/signin", async (req, res)=> {
const { reqEmail, reqPassword } = req.body; //destructuring so less thing to write at the next step
if(!reqEmail || !reqPassword) {
return res.status(400).json({message: "Plz fill the required data"});
}
try {
const foundUser = await User.findOne({email: reqEmail})
if(!foundUser) {
return res.status(400).json({message: "Wrong username or password!"})
}
const result = await bcrypt.compare(reqPassword, foundUser.password);
if(!result){
return res.json({message: "Wrong username or password!"})
} else {
const accessToken = jwt.sign(
{ user_id: foundUser._id, email: foundUser.email},
process.env.TOKEN_KEY,
{ expiresIn: "720h",}
);
// I am confuse what are you trying to do here, in your place I would set up on the cookie since you do that on your authentification.
res.cookie("jwt", accessToken, {
maxAge: 60000, // 60 sec for testing
httpOnly: true,
sameSite: false, //false only for dev
secure: false, //false only for dev
})
res.status(200).json(foundUser);
};
} catch (error) {
return res.status(500).json({message: `${error}`})
}
Than the authentification middleware :
// ...
const Authenticate = (req, res, next) => {
const accessToken = req.cookies.jwt
if(!accessToken) {
return res.status(401).json({error: "Unauthorized: No token provided"});
}
try {
const user = jwt.verify(accessToken, process.env.TOKEN_KEY)
if(user) {
req.user = user
return next();
}
} catch (error) {
return res.status(403).json({error: "Forbidden token error"})
}
}
about page component it's simple for now since you don't manage any state
const navigate = useNavigate();
const callAboutPage = async() =>{
try {
const res = await fetch("/about",{
headers: {
"Content-Type": "application/json"
},
credentials: "include"
});
if(res.status === 200){
const data = await res.json();
// set up the state for rendering
console.log(data);
} else {
// you can also create a state to catch the error message from the backend, in this case the response json should be move to above the if statement.
throw new Error("You must log in to get access")
// than you can display this error message, or from the backend using state for this bloc, and the catch bloc
// navigate to /login
}
} catch (err) {
console.log(err);
navigate("/login");
}
}
router.use(cookieParser());
Try to use cookieParser with app.use instead. (app from express instense)
Expample:
const app = express();
app.use(cookieParser());
and try to put it before server listening in index.js or app.js file.
Hope it help.

JWT Authorization is failing for all endpoints

So I am creating a social media application.
I used JWT token for verification on all endpoints. It's giving me custom error of "You are not authorized, Error 401"
For example: Create post is not working:
This is my code for JWT
const jwt = require("jsonwebtoken")
const { createError } = require ("../utils/error.js")
const verifyToken = (req, res,next) => {
const token = req.cookies.access_token
if(!token) {
return next(createError(401,"You are not authenticated!"))
}
jwt.verify(token, process.env.JWT_SECRET, (err,user) => {
if(err) return next(createError(401,"Token is not valid!"))
req.user = user
next()
}
)
}
const verifyUser = (req, res, next) => {
verifyToken(req,res, () => {
if(req.user.id === req.params.id || req.user.isAdmin) {
next()
} else {
return next(createError(402,"You are not authorized!"))
}
})
}
const verifyAdmin = (req, res, next) => {
verifyToken(req, res, next, () => {
if (req.user.isAdmin) {
next();
} else {
return next(createError(403, "You are not authorized!"));
}
});
};
module.exports = {verifyToken, verifyUser, verifyAdmin}
This is my createPost API:
const createPost = async (req, res) => {
const newPost = new Post(req.body);
try {
const savedPost = await newPost.save();
res.status(200).json(savedPost);
} catch (err) {
res.status(500).json(err);
}
}
Now, in my routes files, I have attached these functions with every endpoints.
For example: In my post.js (route file)
//create a post
router.post("/", verifyUser, createPost);
When I try to access it, this is the result
But, when I remove this verify User function from my route file, it works okay.
I have tried to re-login (to generate new cookie) and then try to do this but its still giving me error.
What can be the reason?
P.S: my api/index.js file https://codepaste.xyz/posts/JNhIr9W6zNnN26CH9xWT
After debugging, I found out that req.params.id is undefined in posts routes.
It seems to work for user endpoints since it contains req.params.id
const verifyUser = (req, res, next) => {
verifyToken(req,res, () => {
if(req.user.id === req.params.id || req.user.isAdmin) {
next()
} else {
return next(createError(402,"You are not authorized!"))
}
})
}
So I just replaced === with || and its working. (but its not right)
if(req.user.id || req.params.id || req.user.isAdmin) {
Can anyone tell me the how can I truly apply validation here since in my posts routes i dont have user id in params

CRUD operations using mongoose and express

I am creating an express app using mongoose with the intention of connecting this to React for the frontend.
I have listed some CRUD operations for a customer controller below but there are a few things I do not like about this approach.
When using Customer.findById with a valid ObjectID that is not found, it returns null with a 200 response code. I want this to return 404 if no customer was found. I realise I could change the catch response to a 404, but I want to have some generic error handling incase the server goes down during the request or an invalid ObjectId was provided, which brings me to my next item.
If I provide an invalid ObjectId I want to provide some meaningful message, is 500 the right response code?
Error handling: Am I returning errors the correct way? currently errors return a string with the error message. Should I return JSON instead? e.g. res.status(500).json({error: error.message). I am planning on connecting this to react (which I am still learning) and I assume the UI will need to display these messages to the user?
findById is repeated in getCustomerById, updateCustomer, and deleteCustomer. I feel this is bad practice and there must be a more streamlined approach?
I want to have one function that validates if the ObjectId is valid. I am aware that I can do this is the routes using router.params but I'm not sure if checking for a valid id should be in the routes file as it seems like something the controller should be handling? See routes example below from another project I did.
What are the best practices and suggested ways to improve my code, based on the above?
I have read the documentation from mongoose, mozilla, and stackoverflow Q&A but they don't seem to address these issues (at least I could not find it).
I am really after some guidance or validation that what I am doing is correct or wrong.
customer.controller.js
const Customer = require("../models/customer.model");
exports.getCustomers = async (req, res) => {
try {
const customers = await Customer.find();
res.status(200).json(customers);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.getCustomerById = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.addCustomer = async (req, res) => {
try {
const customer = new Customer(req.body);
await customer.save().then(res.status(201).json(customer));
} catch (error) {
res.status(500).send(error.message);
}
};
exports.updateCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
Object.assign(customer, req.body);
customer.save();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.deleteCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
await customer.remove();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
Router.params example
This is a routes file (not related to my current app) and is provided as an example of how I have used router.params in the past.
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
router
.route("/")
.get(loginRequired, getArtists) // Get all artists
.post(loginRequired, addArtist); // Create a new artist
router
.route("/:id")
.get(loginRequired, getArtistById) // Get an artist by their id
.put(loginRequired, updateArtist) // Update an artist by their id
.delete(loginRequired, deleteArtist); // Delete an artist by their id
router.param("id", async (req, res, next, id) => {
// Check if the id is a valid Object Id
if (mongoose.isValidObjectId(id)) {
// Check to see if artist with valid id exists
const artist = await Artist.findOne({ _id: id });
if (!artist) res.status(400).json({ errors: "Artist not found" });
res.locals.artist = artist;
res.locals.artistId = id;
next();
} else {
res.status(400).json({ errors: "not a valid object Id" });
}
});
module.exports = router;
i personly like to make error handeling more global so i would write something like
constPrettyError = require('pretty-error')
const pe = new PrettyError()
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
return res
.status(err.status || 500)
.json({ error: { message: err.message || 'oops something went wrong' } })
}
module.exports = errorHandler
as a handler
the in your index / server file
app.use(errorHandler)
then in your handlers just
} catch (err) {
next(err);
}
as an example
if (!artist) next({ message: "Artist not found" ,status:404 });
also, note that you can customize this error handler to switch case (or object) a custom error per status as well if you want
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
const messagePerStatus = {
404: 'not found',
401: 'no authorization'
}
const message = messagePerStatus[err.status]
return res
.status(err.status || 500)
.json({
error: { message: message || err.message || 'oops something went wrong' }
})
}
then just
if (!artist) next({status:404 });
I also agree with answer by Asaf Strilitz but still need to show what i do in my projects
Create a custom error class
AppError.js
class AppError extends Error {
constructor(statusCode, message) {
super();
// super(message);
this.statusCode = statusCode || 500 ;
this.message = message || "Error Something went wrong";
}
}
module.exports = AppError;
Create an error handling middleware
errors.js
const AppError = require("../helpers/appError");
const errors = (err, req, res, next) => {
// console.log(err);
let error = { ...err };
error.statusCode = error.statusCode;
error.message = error.message;
res.status(error.statusCode).json({
statusCode: err.statusCode,
message: err.message,
});
};
exports.errors = errors;
Create a middleware to validate object id
validateObjectId.js
const mongoose = require("mongoose");
const AppError = require("appError");
module.exports = function (req, res, next) {
const { _id } = req.params;
if (_id && !mongoose.Types.ObjectId.isValid(_id)) {
throw new AppError(422, "Invalid ID field in params");
}
next();
};
In your app.js
const { errors } = require("errors");
// At the end of all middlewares
// Error Handler Middleware
app.use(errors);
In your routes file
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const validateId = require("validateObjectId");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
// Your routes
router
.route("/:id")
.get(validateId, loginRequired, getArtistById) // Get an artist by their id
.put(validateId, loginRequired, updateArtist) // Update an artist by their id
.delete(validateId, loginRequired, deleteArtist); // Delete an artist by their id
module.exports = router;
Now regarding findById method being repeated i dont see anything bad in that as it is specific to database call still you can introduce a staic method on model itself or create a single method on cntroller but still need to check if it returns the found object or not and handle the error on that.

express/node: Cannot set headers after they are sent to the client

I'm having some issues with my expressJS application, posting to one route will always result in Cannot set headers after they are sent to the client - I don't understand why and where I'm sending a request/response twice.
I tried playing around with async and await in my functions to get rid of this error but ultimately it's always coming back. I'm also writing an ID to a database, I thought this would be the issue. But I don't think so, because I'm basically just returning a code and not even checking the dynamodb.put request in my current function.
async function putNewUrl(inputUrl) {
const newId = await getId();
const short = ShortURL.encode(newId);
const params = {
TableName: URL_TABLE,
Item: {
long: inputUrl,
short,
},
};
try {
const data = await dynamoDb.put(params).promise();
return short;
} catch (error) {
console.log(error);
}
return short;
}
app.post('/submit', async (req, res) => {
const inputUrl = req.body.url;
try {
const shortUrl = await putNewUrl(inputUrl);
console.log(shortUrl)
return res.json({ success: true, message: shortUrl });
} catch(error) {
console.log(error)
return
}
});
here are my imports:
import { config, DynamoDB } from 'aws-sdk';
import { json } from 'body-parser';
import express from 'express';
import helmet from 'helmet';
import { URL } from 'url';
const app = express();
app.use(helmet());
this is how I start my server
app.listen(3000, () => { console.log('app running'); });
solved it:
there was another route like this:
app.post('/submit', (req, res, next) => {
const inputUrl = req.body.url;
findExistingUrl(inputUrl, (error, data) => {
if (error) {
return res.json({ success: false, message: 'server error' });
}
if (typeof data.Items[0] === 'undefined' && data.Items[0] !== null) {
next();
} else {
return res.json({ success: true});
}
});
});
where I was calling next() right at the end again.
solved it:
there was another route like this:
app.post('/submit', (req, res, next) => {
const inputUrl = req.body.url;
findExistingUrl(inputUrl, (error, data) => {
if (error) {
return res.json({ success: false, message: 'server error' });
}
if (typeof data.Items[0] === 'undefined' && data.Items[0] !== null) {
next();
} else {
return res.json({ success: true});
}
});
});
where I was calling next() right at the end again.

Does not work post-query when login

I'm trying to verify the user's password using bcrypt. But, unfortunately, my post-request does not work, it just loads for a long time and that's it.
I have a model user.js with this code:
UserSchema.methods.comparePasswords = function (password) {
return bcrypt.compare(password, this.password);
};
And i have a controller auth.js with this code:
export const signin = async (req, res, next) => {
const { login, password } = req.body;
const user = await User.findOne({ login });
if (!user) {
return next({
status: 400,
message: 'User not found'
});
}
try {
const result = await user.comparePasswords(password);
} catch (e) {
return next({
status: 400,
message: 'Bad Credentials'
});
}
req.session.userId = user._id;
req.json(user);
};
The handling of incorrect input works well and the server returns false messages for me, but does not process the correct input.

Categories

Resources