Next.js's API route does not send response - node.js

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

Related

express status code not updating, getting json message update

I am not getting status message as 500 eventhough I set. always getting status message as 200. how to set the status as 500?
here is my code : "express": "4.17.2",
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.json({ message: 'User already exist', success: false }).status(500);
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.json({ message: 'user created successfully', success: true });
} catch (error) {
res.sendStatus(500).json({ message: 'Error creating user', success: false });
}
});
react axios:
when i use:
return res.status(500).json({ message: 'User already exist', success: false }); getting something went wrong
export const registerUser = createAsyncThunk('post/user', async (user: RegisterFormProps) => {
try {
const response = await axios.post(environment.BASE_URL + '/user/register', user);
console.log('suc', response.data.success);
if (response.data.success) {
toast.success(response.data.message);
} else {
toast.error(response.data.message);
}
} catch (error) {
const err = error as AxiosError;
console.log('err', err);
toast.error('something went wrong');
}
});
You should be using res.status instead of res.sendStatus in your code.
res.status(statusCode) just sets the status on the response.
whereas res.sendStatus(statusCode) sends the response after setting the status.
for example:
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
on the client side try using error.response.data in your catch block
Try this one:
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.status(500).json({ message: 'User already exist', success: false });
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.status(201).json({ message: 'user created successfully', success: true });
} catch (error) {
res.status(500).json({ message: 'Error creating user', success: false });
}
});

How to switch back to async/await?

I am new to async/await so I have an exercise like below code, I have converted this code to async/await many times and still no success. Please help me. Thanks very much!
My code is as follows:
exports.register = (req, res) => {
const user = req.body;
try {
// Validate the registration form
validateRegisterForm(user)
.then((response) => {
// If response is true, hash the password
if (response) {
Md5Password(user.password)
.then(async (hash) => {
const { name, email } = user;
const newUser = new User({
name,
password: hash,
});
// Save the user
const savedUser = await newUser.save();
res.status(200).json(savedUser);
})
.catch((error) => {
res.status(500).json({
message: error.message,
err: "500: Internal Server Error",
});
});
}
// But if response is false, show the error message
else {
res.status(401).json({
message: errorMessage(),
error: "401: Unauthorized",
});
}
})
.catch((error) => {
res.status(500).json({
message: error.message,
err: "500: Internal Server Error",
});
});
} catch (error) {
res.status(500).json({
error: error.message,
message: "registration failed",
e: "500: Internal Server Error",
});
}
};
Please help me, thanks a lot!
Not sure exactly what you're trying to achieve, but here's a version of your code with async/await:
exports.register = async (req, res) => {
const user = req.body;
try {
// Validate the registration form
const response = await validateRegisterForm(user);
// If response is true, hash the password
if (response) {
const hash = await Md5Password(user.password);
const { name, email } = user;
const newUser = new User({
name,
password: hash,
});
// Save the user
const savedUser = await newUser.save();
res.status(200).json(savedUser);
} else {
res.status(401).json({
message: errorMessage(),
error: "401: Unauthorized"
});
}
} catch (e) {
res.status(500).json({
message: e.message,
err: "500: Internal Server Error"
});
}
}

Express.js custom error handling middleware isn't working for a few errors

Custom error handler:
export const errorHandler: ErrorRequestHandler = (err, _, res) => {
if (err instanceof HttpError) {
res.status(err.statusCode).json({
message: err.message
});
return;
}
res.status(500).json({
message: err.message,
});
};
Handler where I throw the error:
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
The error handler middleware works as expected when everywhere except when it is thrown in catch block of the registerHandler. It's driving me crazy. Can somebody explain why this is so?
middlewares are pipes, in other words functions that runs after another function, so if you want to run an error handler you need to pass the next function to run
export const registerHandler: Handler = async (req, res, next) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
// to let express know that the next function to run is an errorhandler you need to pass a parameter to the function next
return next(new UnprocessableEntity());
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
to create error handlers you need to create a function with 4 parameter
error: the error
req: request
res: response
next: next handler
function errorHandler (error, req, res, next) {
if (err instanceof HttpError) {
return res.status(err.statusCode).json({
message: err.message
});
}
return next(error);
}
for this to work you need to specify your error handlers after all your routes
const app = express()
app.use("/api", apiRoutes());
app.use("/more-routes", moreRoutes());
app.use(errorHandler);
app.use(anotherErrorHandler);
This might not be the exact solution you may be looking for, but it might help.
The express js documentation
says:-
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.
So you don't need try and catch at all.
The above code can be written as:-
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
};
If any error occurs in await line, the errorHandler will automatically be called, and you don't have to explicitly throw an error.

router.patch is returning 404 "not found"

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

Getting 400 Bad Request despite catching the err in angular2+

I have designed a login page where login is successful when i put a correct login and password and Login doesn't happen when I put an incorrect username or password which is correct. However, I get this error:
POST http://localhost:3003/login/authenticate 400 (Bad Request)
ERROR HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: "Bad Request", url: "http://localhost:3003/login/authenticate", ok: false, …}
Everything works properly however, I get the error in the console. Like this:
I want the 400 bad request error to not appear in the console. How to do that?
login.component.ts
login(data) {
console.log("Inside Login");
this.authenticateObj = {
username: data.username,
password: data.password
}
this.http.post("http://localhost:3003/login/authenticate",
this.authenticateObj)
.map(Response => Response)
.catch((err) => {
console.log("err =", err)
alert('Login Failed. Username or Password
is incorrect');
return Observable.throw(err);
})
.subscribe((res: Response) => {
console.log("Inside login authenticate subscribe");
this.info = res;
if (this.info.message == 'Login Successful.') {
console.log("test after login = ", this.info);
if (localStorage.getItem('username') &&
localStorage.getItem('token')) {
alert('Login Successful');
this.router.navigate(['/file-upload/wsdl']);
} else {
this.notification.Error('Unauthorized');
}
}
if (this.info.message == 'error') {
alert('Login Failed');
}
else if (this.info.status == 400) {
alert('Login Failed');
}
})
}
login.controller.js
function authenticateUser(req, res, next) {
console.log("Inside authenticateUser = ", req.body)
LoginService.authenticate(req,req.body)
.then(function (token) {
if (token) {
res.setHeader("authorization",token.token);
res.send({
message: 'Login Successful.',
response: token
});
} else if(res.message == 'Username or Password is
incorrect'){
res.status(401).send({
message: 'Unauthorized. '
});
}
else {
console.log("inside controller, else res.status-400");
res.status(400).send({
message: 'Username or password is incorrect'
});
}
})
.catch(function (err) {
console.log("inside controller, catch res.status 400")
// res.status(400).send(err);
res.status(400).send({
message: 'Username or password is incorrect'
});
});
}
In order to handle errors from server properly, you have to catch them in the subcribe() method of the Observable returned by http.post from Rxjs:
this.http.post("http://localhost:3003/login/authenticate", this.authenticateObj)
.subscribe(
(res: Response) => {
// code when no error...
},
err => {
// error handling...
},
() => {
// finally...
}
);
IMO Bad request is an incorrect response by your server for incorrect username/password combination. You can return a "401" or a "200" itself depending on your requirement.
Now if you want the error not to appear in the console then add an error callback in your subscribe().
this.http.post("http://localhost:3003/login/authenticate", this.authenticateObj)
...
// rest of the code
.subscribe((res: Response) => {
// your code
}, (error) => {
// handle the error here, show some alerts, warnings, etc
console.log(error)
})

Resources