Problem wiring a nodejs express back-end with a reactjs front-end - node.js

I am trying to put together a reactjs Dashboard and wire it up with a Nodejs back-end. I am currently trying to validate a jwt token. when I do it using a Postman app, its working fine. but when I try it using my reactjs form, its not happening. please help me find the problem in my code. I am not an experienced developer. I am kind of a newbie to both nodejs and reactjs.
Any help will be highly appreciated.
I will try to post all the relevant code and some snapshots below.
//reactjs code calling this function on a button submit
//verify user
onVerify = event => {
let databody = localStorage.getItem("jwtToken");
event.preventDefault();
console.log(databody);
fetch("http://localhost:8000/api/auth/me", {
method: "get",
headers: {
"x-access-token": databody
}
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error("Something went wrong with your fetch");
}
})
.then(json => {
console.log(json.token);
});
};
Nodejs express backend code
//app.js
var express = require("express");
var db = require("./db");
var Cors = require("cors");
var app = express();
app.use(Cors());
var UserController = require("./user/UserController");
app.use("/users", UserController);
var AuthController = require("./auth/AuthController");
app.use("/api/auth", AuthController);
var WorkInvenController = require("./auth/WorkInvenController");
app.use("/api/workinven", WorkInvenController);
module.exports = app;
//AuthController.js
router.get("/me", function(req, res) {
console.log(req.headers);
var token = req.headers["x-access-token"];
if (!token)
return res.status(401).send({ auth: false, message: "No token provided." });
jwt.verify(token, process.env.secret, function(err, decoded) {
if (err)
return res
.status(500)
.send({ auth: false, message: "Failed to authenticate token." });
User.findById(decoded.id, { password: 0 }, function(err, user) {
if (err)
return res.status(500).send("There was a problem finding the user.");
if (!user) return res.status(404).send("No user found.");
res.status(200).send(user);
});
});
});
terminal output from the backend when posted using Postman
terminal output from the backend when posted using reactjs from
browser error attached below

as i can see its a "rejection error"
you can just add .catch() and handle the error that is being thrown.
onVerify = event => {
let databody = localStorage.getItem("jwtToken");
event.preventDefault();
console.log(databody);
fetch("http://localhost:8000/api/auth/me", {
method: "get",
headers: {
"x-access-token": databody
}
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error("Something went wrong with your fetch");
}
})
.then(json => {
console.log(json.token);
}).catch(error=>{
//handle the error in here may be
console.log(error)
});
};
sometime we wrote some sort of wrapper as well to handle errors:
fetch(<url>, {
<headers>,
<options>
})
.then(this._checkStatus)
.then(response => response.json());
_checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
// Success status lies between 200 to 300
return response;
} else {
//here handle the error may be.
var error = new Error(response.statusText);
console.log(error);
}
}
so in your case, when the error coming its not handled , as there no .catch found, its throwing unhandled rejection error.
you can look at the above methods and follow any of them , also let me know if you are still facing issue with this.

sorry that I didn't post the whole code up here, or else I am sure you guys would have figured it long time back. long story short..
the problem was with my token, I accidentally set the expiresIn to 60 thinking that it was 60 minutes instead of 60 * 60. so the token was expiring every next minute.
Now after setting the token expiry to "24h" everything seems working good..
thanks a lot for you help guys.

Related

Status 404 instead of 400 when testing with Superagent / Supertest / Jest

Situation
I am building and testing a simple api with express. One of the routes is /api/blogs/:id, where the api should return a status 400 if the provided id is in the wrong format, and 404 if the id is not present in the database.
Problem
The api works fine and responds with the right status codes when making requests by browser or the REST client plugin in vsCode. But when I make requests with a malformed id via Superagent in my unit tests, the server responds with a 404, when it should be a 400.
Route
blogsRouter.get('/:id', async (req, res, next) => {
try {
const { id } = req.params;
const blog = await Blog.findById(id);
if (blog) {
res.status(200).json(blog);
} else {
res.status(404).end();
}
} catch (error) {
next(error);
}
});
Normally, Mongoose throws an error when running Blog.findById(id), triggering catch (error) { next(error) }, which executes the next middleware function errorHandler. For some reason though, this doesn't seem to happen when testing.
errorHandler
const errorHandler = (error, req, res, next) => {
if (error.name === 'CastError') {
res.status(400).json({ error: 'malformatted id' });
return;
} if (error.name === 'ValidationError') {
console.log(error.message);
res.status(400).json({ error: error.message });
return;
}
next(error);
};
test
test("fails with status 400 when 'id' has wrong format", async () => {
const invalidId = '45234sdsdasf';
await api.get(`/api/blogs/${invalidId}`)
.expect(400);
});
I found the solution.
The problem was const invalidId = '45234sdsdasf'.
This should be a malformed id, but for some reason Mongoose sees 12 characters long strings as a correct objectId. Changing invalidId to something else solved the problem.

JSON.parse: unexpected character at line 1 column 1 of the JSON data when serving the build

i'm struggling to fix json parse errors in my PERN app, i really don't know what's wrong with the code, cause it's throwing errors when i serve the build. I send the json response after the authentication and if i check on postman i see that everything is correct when i'm in developing mode.
async function isAuth() {
try {
const response = await fetch("http://localhost:5000/auth/verify", {
method: "GET",
headers: { token: localStorage.token }
})
const parseRes = await response.json()
parseRes === true ? setIsAuthenticated(true) : setIsAuthenticated(false)
} catch (error) {
console.error(error.message)
}
}
This is the method i use to check if the token is correct or not (i'm using jwt tokens for authentication).
The error is here :
const parseRes = await response.json()
The server code is this:
router.get("/verify", authorization, async (req, res) => {
try {
res.json(true)
} catch (error) {
console.error(error.message);
}
})
This method use the authorization middleware to check if the token is correct or not.
Authorization middleware:
const jwt = require("jsonwebtoken")
require("dotenv").config()
module.exports = async (req, res, next) => {
try {
const jwtToken = req.header("token")
if (!jwtToken) {
return res.status(403).json("Not Authorized")
} else {
const payload = jwt.verify(jwtToken, `${process.env.ACCESS_TOKEN_SECRET}`)
req.user = payload.user
next()
}
} catch (error) {
console.error(error.message);
res.status(403).json("Not Authorized")
}
}
I have the same error when i'm trying to do the login and the registration.
If you need more code just tell me, i really need some help.
Thank you in advance.
try add the following header:
Content-Type: 'application/json'
happen to me one time and this line fix it
Update:
the problem is probably localStorage.token you cant access like this localStorage and can only be by localStorage.getItem("token")
and update and add response without the .json() so we can check why we get what you get.

.save() is not a function (Mongoose)

So, I'm not 100% why this isn't working as intended. I have an Edit Profile React component (I'm learning how to build a SSR-based application currently, using the MERN stack) - but when I submit the edit, I get an error that "user.save is not a function - Code:
From the routes:
router.route('/api/users/:userId')
.get(authCtrl.requireSignin, userCtrl.read)
.put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
.delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
The API Helper:
const update = async (params, credentials, user) => {
try {
let response = await fetch('/api/users/' + params.userId, {
method: 'PUT',
headers: {
"Accept": 'application/json',
Authorization: 'Bearer ' + credentials.t
},
body: user
})
return await response.json()
} catch (err) {
console.log(err)
}
}
And lastly, the actual controller, that handles all the logic behind the update: (This function sanitizes the password information before passing it back to the client, hence the undefineds)
const update = (req, res) => {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
if (err) {
return res.status(400).json({
error: "Photo could not be uploaded"
})
}
let user = req.profile
user = extend(user, fields)
user.updated = Date.now()
if(files.photo){
user.photo.data = fs.readFileSync(files.photo.path)
user.photo.contentType = files.photo.type
}
try {
await user.save()
user.hashed_password = undefined
user.salt = undefined
res.json(user)
} catch (err) {
console.log(err)
return res.status(400).json({
error: errorHandler.getErrorMessage(err)
})
}
})
}
This isn't a production level application, just for me learning how to do this from scratch (without CRA, and all contained in one project using SSR)
EDIT: After some digging, console.logs and console.dirs, I discovered that the updates passed from the component aren't even being passed to the controller. The stale data (from the database) are logging, but req.profile is completely empty. I may re-visit this code completely and make some major changes to it.. All part of learning, right?
Here are the auth methods that were requested (I'm using Session Storage for now, but that may change to localStorage):
import User from '../models/user.model'
import jwt from 'jsonwebtoken'
import expressJwt from 'express-jwt'
import config from './../../config/config'
const signin = async (req, res) => {
try {
let user = await User.findOne({email: req.body.email})
if (!user) {
return res.status(401).json({error: "User not found"})
}
if (!user.authenticate(req.body.password)) {
return res.status(401).send({error: "Email and Password do not match"})
}
const token = jwt.sign({_id: user._id}, config.jwtSecret)
res.cookie('t', token, {expire: new Date() + 9999})
return res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email
}
})
} catch (err) {
return res.status(401).json({error: "Could not sign in"})
}
}
const signout = (req, res) => {
res.clearCookie('t')
return res.status(200).json({message: "Signed out"})
}
const requireSignin = expressJwt({
secret: config.jwtSecret,
algorithms: ['sha1', 'RS256', 'HS256'],
userProperty: 'auth'
})
const hasAuthorization = (req, res, next) => {
const authorized = req.profile && req.auth
&& req.profile._id == req.auth._id
if (!(authorized)) {
return res.status(403).json ({error: "User is not authorized"})
}
next()
}
export default {
signin,
signout,
requireSignin,
hasAuthorization
}
Possible places where you could have a mistake: (code is not shown)
If your req.profile isn't a mongoose object, this won't work
let user = req.profile
From your other posts, I think you're probably getting req.profile from your jwt. That means this is not a mongoose object. What you'll need to do is either:
As you mentioned, use findByIdAndUpdate passing the id and the object to be updated. Note that if you have a mongoose middleware for save it won't run here
Do a user = await User.findById(id), update the user as you see fit, then use user.save. This gives you a bit more control over it, but runs 2 operations.
This has been solved.. My issue was apparently with the form not passing the request body properly to the API, which was caused by a faulty install of a dependency. Once I got that solved, the rest fell into place, and I can now do what I need to do with ease..
Thank you all who attempted to troubleshoot this with me.

How to properly log erros in my application using ExpressJS

I am part of a project which uses nodeJS + ExpressJS for the backend application, and We have a middleware function to log accesses on routes in the database.
When an User tries to access the /user route with a post method, a middleware receives the Request, get information like the URL, ip address, origin, a description of the event and record it in the database.
Everything works just fine, but some of my teammates were discussing about how to log the erros also in the database.
I will put bellow a code example
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
return response.status.json({ error: 'E-mail already in use' });
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
response.status(500).json({ error });
}
};
When we were discussing about how to implement it, we realized we'd have to call a log error function in a lot of places since we have many flows which leads to an error response.
So the code would be just like:
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
function() // here we would log the error
return response.status.json({ error: 'E-mail already in use' });
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
function() // here we would log the error
response.status(500).json({ error });
}
};
is it a properly way of dealing with error logging or is there any better way of doing it? Thank you for reading!
You can use the built-in error handler provided by Express.JS for this kind of logic, of course it requires a bit of setup. Like most things in Express.JS, the error handler it's just a middleware function with four parameters err, req, res and next, which MUST be placed after all your other middlewares. It comes to play when, inside a router handle (for example), your call next(err) (where err it's an Error) or by simply throwing err. Check out the documentation for more.
app.use(...)
app.use(...)
app.use((req, res, next) => {
if (req.params.id === undefined) {
let error = new Error("ID required.")
error.statusCode = 400
error.statusMessage = "Request not valid, ID not found."
throw error;
} else {
// Do some stuff...
}
})
// NOTE: After ALL your other middlewares
app.use((err, req, res, next) => {
console.error(err)
res
.status(err.statusCode)
.json(err.statusMessage)
})
Ideally you should log the errors only inside the catch block. Whenever you encounter an error just throw a new error by calling throw new Error("Type your error message here"). Then your function inside catch block will log and handle the error appropriately.
I would change your code to this:
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
throw new Error("E-mail already in use")
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
function() // log your error
response.status(500).json({ error.message });
}
};
Read more about Errors here.

Unexpected token in JSON argument in POST request

I am trying to make a POST request to a nodejs server from an Ionic application work, but I stumbled on this error.
Http failure response for http://127.0.0.1:3000/services/user/signin: 500 Internal Server Error", SyntaxError: Unexpected token u in JSON at position 0;
I have access to both the application and the API server. Task at hand, trying to send credentials to the server, which will check if those credentials are recognized and will send a response. The server side code works just fine, since I have a web app accessing to the same resources and working like a charm.
Here goes the code.
Home page:
doLogin() {
this.remoteService.login(this.user);
}
user is a key value array
user = { email:'', password:'' };
Now for the login function in the remoteService injectable:
login(user){
let headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
console.log(JSON.stringify(user));
this.http.post(serviceUrl+'/user/signin', JSON.stringify(user), {headers: headers}).subscribe(
function(response) { console.log("Success Response" + response)},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log(err);
} else {
console.log(err);
}
}
);
}
I used this line
console.log(JSON.stringify(user));
to test the function argument for JSON correctness, and it's a charm.
Finally, this is the nodejs server code
if(label === 'signin') {
return function (req, res, next) {
{
var user = JSON.parse(req.query.user);
db.query('SELECT username as id FROM user where email = ? and password = ?', [user.email,user.password], function (err, result) {
if (err) {
next(err);
}
else {
if(result.length === 1) {
delete user.password;
req.session.user = result[0];
req.result = 400;
}
else
{
req.result = 404;
}
next();
}
});
}
};
}
Could you please help me fix the nasty error? I have been banging my head on this all day long and a big part of the last night.

Resources