I am learning how to set up a Express server and have run into an odd issue when creating new users. The POST request goes through and gives no error and takes me to my dashboard but when I look in the sqlite DB it shows all values from the registration form saved as undefined. I have installed body-parser and called it properly into the server, I believe.
I have tried changing extended: true instead of false and added adding res.setHeader('Content-Type', 'text/plain') into the app.use as stated by body-parser guide but it still saves as undefined every time.
here is my server.js code
const app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader('Content-Type', 'text/plain');
next();
});
here is the POST code in the server.js
app.post('/register', function(req, res) {
bcrypt.hash(req.body.password, saltRounds, function(err, hash) {
let db = new sqlite3.Database("./database/InvoiceApp.db");
let sql = `INSERT INTO users(name, email, company_name, password) VALUES ('${req.body.name}','${req.body.email}','${req.body.company_name}','${hash}')`;
db.run(sql, function(err) {
if (err) {
throw err;
} else {
return res.json({
status: true,
message: "User Created"
});
}
});
db.close()
});
});
and this is my form code, i am using vue.js
register () {
const formData = new FormData()
let valid = this.validate()
if (valid) {
formData.append('name', this.model.name)
formData.append('email', this.model.email)
formData.append('company_name', this.model.company_name)
formData.append('password', this.model.password)
this.loading = "Registering you, please wait"
axios.post('http://localhost:3128/register', formData).then(res => {
this.loading = ''
if (res.data.status === true) {
this.$router.push ({
name: 'dashboard',
params: { user: res.data.user }
})
} else {
this.status = res.data.message
}
})
} else {
alert('Passwords do not match')
}
}
I get no error messages when creating a new user, only if I try and login as a created user or do other actions linked to the created user. Why is my form saving as undefined in the sqlite DB?
You should use JSON format from your Vue app instead of FormData.
FormData is an object that passes with Content-Type application/x-www-form-urlencoded.
And you body parser is configured for the Json.
Please try to replace
formData.append('name', this.model.name)
formData.append('email', this.model.email)
formData.append('company_name', this.model.company_name)
formData.append('password', this.model.password)
with an actual JS object
{
"name": this.model.name
.....other fields.....
}
and pass this object or this object stringified with JSON.strinfigy(obj);
After spending a day researching FormData and JSON I figured it out thanks to this post and the answer by M U. Because I was using Vue.js and had everything from the form stored using v-model all I had to do was update the post method to:
register () {
let valid = this.validate()
if (valid) {
this.loading = "Registering you, please wait"
axios.post('http://localhost:3128/register', this.model).then(res => {
this.loading = ''
if (res.data.status === true) {
this.$router.push ({
name: 'dashboard',
params: { user: res.data.user }
})
} else {
this.status = res.data.message
}
})
} else {
alert('Passwords do not match')
}
}
and the whole form uploaded to the database with the password hashed
Related
Introduction
So, I'm using the MERN stack (with Heroku + Netlify), and I'm having some really strange consistency problems with how the DELETE request is being handled. I've tried countless solutions in the last
three days trying to get this to work and none of them have worked. A lot of these solutions have
come from stack overflow, so if you want to direct me to another post, the chance is that I've already seen it. I've scoured every part of the web and making this post is my last resort.
The Problem
So, when I make a delete request, I'm getting the per-usual OPTIONS request since I'm sending a token in a custom header of the request ('x-auth-token'). The OPTIONS request always resolves with a 204, meaning that everything should be alright. However, afterward, there is no DELETE request like there should be. This, in essence, is my problem. I've checked my Heroku logs, and all I can see is the OPTIONS request, and nothing else.
Inconsistencies?
So this is where I've been very confused. The thing is, that sometimes it DOES work. And other routes I use in my API (like login, and creating a new post) work, even though I'm using the same middleware.
Every time it works, I get the OPTIONS request and then the DELETE request (with a 200 status) like I would expect to happen.
If you want an example of a re-creatable scenario:
I create X number posts after logging in and getting a valid token, then I can see those posts rendering in the posts listing on my home page. I then navigate one of the posts and delete it by clicking and then a confirmation button. I automatically get redirected to the next post in the list. I repeat this till I get to the last post. I delete that post, and since there are no more posts left, I get redirected to the posts listing which is... not empty! The last post I tried deleting is still there.
Keep in mind, that the DELETE requests all get sent in exactly the same way, so I'm pretty sure this isn't a front-end issue, so no need to poke around in the code there. I've logged everything and debugged, and it's 100% consistent with what I would expect.
(The create post doesn't redirect, while the delete post does? I don't see how this would effect anythign as the DELETE request gets sent as per usual... Though maybe a solution lies within this fact.)
Solutions I've tried
Cors
First off, you might already be rushing to your keyboard to tell me that this is a CORS issue. I thought the same thing yesterday, but I'm not so sure now. I've tried messing with all the config settings possible in CORS to get this to work. Since my two websites are on different domains, then CORS verifies the requests. I've already added my front-end website to a whitelist, and all other requests are going through properly, so no problem there. I've tried adding an allowHeaders option in the config, but it didn't do anything more than the default setting. I've also added 'OPTIONS' to the allowed methods in the config, still nothing. I'm also using app.use(cors({config})). I'll include some code later to see some more of this in detail.
Debugging
I've basically tested things out by inserting console.logs everywhere and discovered that neither the middleware, the options route (I tried making an options route with same route url), or the original post route get executed when the OPTIONS request doesn't result in a DELETE request.
Static Server
This is maybe where some of my inexperience shows (this is my first Web project). I saw some solutions telling that a static server is needed. So I tried setting up a static server, but I didn't see any results. So I'm not too sure what this accomplished.
Async and Await
I was just trying things at this point, so I made all my routes async to see if it would do anything. It didn't.
Others
I've also messed around with environment variables and dotenv, and other stuff I can't remember. I think everything here should already be sufficient information understand the situation.
Code
index.js
const express = require('express');
require("dotenv").config({ path: "variables.env" });
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const routes = require("./routes/router");
const cors = require("cors");
const morgan = require('morgan')
const app = express();
const whitelist = [
process.env.ORIGIN
];
app.use(
cors({
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
console.log(origin);
callback(new Error("Not allowed by CORS"));
}
}, //frontend server localhost:3000
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true, // enable set cookie
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(morgan('dev'));
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('connected to db');
});
const userSchema = mongoose.Schema({
name: String,
password: String
});
// Routes
// TODO: make seperate routers/routes
app.use("/", routes);
// Serve static assets if in production
if (process.env.NODE_ENV === 'production') {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
// TODO: set up custom port in future
app.listen(process.env.PORT, () => console.log(`Server listening at http://localhost:${process.env.PORT}`));
// Callback functions?
router.js
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
const User = require('../models/User');
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const auth = require('../middleware/auth');
const adminAuth = require('../middleware/adminAuth');
const cors = require("cors");
require("dotenv").config({ path: "variables.env" });
// import 'moment'
// second onwards are handlers => triggers like the post body then next() to go to the next handler
router.post('/api/add_post', adminAuth, async (req, res, next) => {
try{
newPost = new Post({
title: req.body.title,
body: req.body.body,
author: req.body.author,
created: req.body.created,
});
const savedPost = await newPost.save();
if (!savedUser) throw Error('Something went wrong saving the post');
res.send(savedPost);
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
router.delete('/api/delete_post/:id', adminAuth, async (req, res, next) => {
// timeout?
// console.log(req.body);
try{
const id = req.params.id;
if(!id) throw Error('Invalid ID');
const post = await Post.findById(id);
if (!post) throw Error('Post doesn\'t exist');
const removed = await post.remove();
if(!removed) throw Error('Problem with deleting the post');
res.status(200).json({ success: true });
} catch(e) {
console.log("Error: ", e.message);
res.status(400).json({ msg: e.message, success: false });
}
});
// TODO : UPDATE for async soon
router.post('/api/update_post', adminAuth, async (req, res, next) => {
const id = req.body._id;
test_post_data = {
title: req.body.title,
body: req.body.body,
author: req.body.author,
modified: req.body.modified,
};
console.log(test_post_data, id);
Post.updateOne({ _id: id }, test_post_data, (err) => {
if(err) return next(err);
return res.status(200);
});
});
router.get('/api/get_posts', async (req, res, next) => {
try{
const posts = await Post.find();
if(!posts) throw Error('Error with fetching the posts')
res.send(posts.reverse());
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
router.get('/api/get_chapter/:id', async (req, res, next) => {
try{
const id = req.params.id;
const post = await Post.findOne({_id: id})
if(!post) throw Error('No post was found')
res.send(post);
} catch(e) {
res.status(400).json({ msg: e.message })
}
});
// User routes
// TODO : make in seperate file
router.post('/api/user/register', async (req, res) => {
const { name, email, password } = req.body;
// Simple validation
if (!name || !email || !password) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
try {
const user = await User.findOne({ email });
if (user) throw Error('User already exists');
const salt = await bcrypt.genSalt(10);
if (!salt) throw Error('Something went wrong with bcrypt');
const hash = await bcrypt.hash(password, salt);
if (!hash) throw Error('Something went wrong hashing the password');
const newUser = new User({
name,
email,
password: hash,
admin: false
});
const savedUser = await newUser.save();
if (!savedUser) throw Error('Something went wrong saving the user');
// TODO : check up on expires stuff : 3600 = 1 hr
const token = jwt.sign({ id: savedUser._id, admin: savedUser.admin }, process.env.JWT_SECRET, {
expiresIn: 3600
});
res.status(200).json({
token,
user: {
id: savedUser.id,
name: savedUser.name,
email: savedUser.email,
admin: savedUser.admin
}
});
} catch (e) {
res.status(400).json({ error: e.message });
}
});
router.post('/api/user/login', async (req, res) => {
const { name, password } = req.body;
// Simple validation
if (!name || !password) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
try {
// Check for existing user
const user = await User.findOne({ name });
if (!user) throw Error('User Does not exist');
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) throw Error('Invalid credentials');
const token = jwt.sign({ id: user._id, admin: user.admin }, process.env.JWT_SECRET, { expiresIn: 3600 });
if (!token) throw Error('Couldnt sign the token');
res.status(200).json({
token,
user: {
id: user._id,
name: user.name,
email: user.email,
admin: user.admin
}
});
} catch (e) {
res.status(400).json({ msg: e.message });
}
});
module.exports = router;
adminAuth.js
const jwt = require('jsonwebtoken')
require("dotenv").config({ path: "variables.env" });
module.exports = (req, res, next) => {
console.log(req.header('x-auth-token'));
const token = req.header('x-auth-token');
// Check for token
if (!token)
return res.status(401).json({ msg: 'No token, authorizaton denied' });
try {
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log('decoded:', decoded);
if(!decoded.admin)
return res.status(401).json({ msg: 'Not an admin, authorization denied' });
// Add user from payload
// console.log('decoded:', decoded);
req.user = decoded;
next();
} catch (e) {
res.status(400).json({ msg: 'Token is not valid' });
}
};
Link for request examples, and Heroku log since Stackoverflow says it's spam:
https://gist.github.com/macklinhrw/b2fec97642882ba406c49cce3e195c39
Edit
I pasted the Chrome request and response headers into the gist at the bottom, but there was no response data to go along with either.
I've debugged a little using this to check the difference and I discovered that with delete action that ends up working, the red (canceled) request has headers, while the non-working is completely empty (filled with 'provisional headers' if that means anything).
I couldn't copy-paste the request headers into the gist for the working red (canceled) one. But, I pasted everything that I thought could possibly be useful from chrome, hopefully it helps.
Also, I didn't see any DELETE requests when I was using the Chrome network tool, and I was seeing them on the other tool. Not sure if it matters, probably just a config option somewhere.
So, I haven't found an exact answer, but I've found a workaround.
As it turns out, it might have something to do with axios, and I've been searching for the wrong things in the last 3 days.
This thread helped me: https://github.com/axios/axios/issues/1428
I've added an e.preventDefault() to the onClick method I use for the delete button.
This fixed the problem, but doesn't redirect (I use href={link}), so I'm going to add a conditional render for react-router to redirect the page. I don't know of a better method so maybe give me some ideas. I'll edit if I have further troubles.
We're trying to integrate Passport authentication with out React app, and we're using React Router.
On the first submission of correct user credentials, the server receives the post, adds a session to our database, and seems to send a response, but the client doesn't update. The username and password show up in the url as a query string. Then when we resend the credentials without removing the query string from the url, the client is able to receive the response from the server.
In other words, if we don't refresh before submitting the login info again, it works.
This is the click handler that our form utilizes:
const handleClick = () => {
return axios.post('/login', { username, password })
.then(({ data }) => {
const { message } = data;
if (message === 'success') {
const { user } = data;
setUserId(user.id);
setUser(user);
}
setAuthStatus(message);
})
.catch(err => console.error(err));
};
This is our server route that is hit on every post request:
loginRouter.post('/', (req, res, next) => {
console.log('stop');
passport.authenticate('local', (err, user, info) => {
const { invalidPassword } = info || false;
if (err) {
return next(err); // will generate a 500 error
}
if (!user) {
return res.send({ message: 'invalidUser' });
}
if (invalidPassword) {
return res.send({ message: 'invalidPassword' });
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ user, message: 'success' });
});
})(req, res, next);
});
This is our Passport Local Strategy that uses Sequelize:
passport.use(new LocalStrategy(
(username, password, cb) => {
User.findOne({ where: { username } })
.then((user) => {
if (!user) {
return cb(null, false);
}
if (validPassword(password, user.hash, user.salt)) {
return cb(null, user);
}
return cb(null, false, { invalidPassword: true });
})
.catch(err => {
cb(err);
});
},
));
Having trouble debugging this... We suspect the error is on the client side and may have to do with React-Router. We are using React-Router and Passport for the first time on this project.
Any help is appreciated!
Welp... All we were missing was event as a parameter in handleClick and event.preventDefault().
I have an express API and a ReactJs front-end. I try to make a POST call from my front-end directly to the local API.
For this I'm using axios.
The request is working fine when I set the parameters directly inside the query string but is always getting on timeout if I try to add the parameters through the data attribute of the axios.post() method.
Working
axios.post(`http://localhost:5001/site/authenticate?username=demo&password=demo`)
Not working
const payload = {
"username":"mh",
"password":"mh"
}
axios.post(`http://localhost:5001/site/authenticate`, payload)
My express server:
const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var cors = require('cors');
const app = express();
const port = process.env.API_PORT || 5001;
app.use(cors());
app.set('secret', process.env.API_SECRET);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use((req, res, next) => {
let data = '';
req.setEncoding('utf8');
req.on('data', (chunk) => {
data += chunk;
});
req.on('end', () => {
req.rawBody = data;
next();
});
});
// Allow CORS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// SITE ROUTES -------------------
const siteRoutes = express.Router();
siteRoutes.post('/authenticate', function(req, res) {
console.log('auth');
getDocument(usersBucket, req.query.username)
.then((doc) => {
console.log("Authentification... TODO");
// return the information including token as JSON
res.json({
success: true,
status: 200,
token: token
});
})
.catch(() => {
res.status(401).json({ success: false, message: 'Authentification failed. User not found.' });
});
});
// route middleware to verify a token
siteRoutes.use(function(req, res, next) {
const token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('secret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.', status: 401 });
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
siteRoutes.get('/', function(req, res) {
res.json({ message: 'Welcome!' });
});
app.use('/site', siteRoutes);
app.listen(port, () => {
logger.log(`Express server listening on port ${port}`);
});
Any idea? Thanks.
Update
I replaced my route just to see if I got in or not (without worrying about parameters):
siteRoutes.post('/authenticate', function(req, res) {
console.log("go in");
res.json({
success: true,
status: 200,
});
});
But my console.log is not showing hen I use the payload (it is when I do not).
You should access the payload data via request.body, not the request.query:
// SITE ROUTES -------------------
const siteRoutes = express.Router();
siteRoutes.post('/authenticate', function(req, res) {
console.log('auth');
getDocument(usersBucket, req.body.username) // <------- HERE
.then((doc) => {
console.log("Authentification... TODO");
// return the information including token as JSON
res.json({
success: true,
status: 200,
token: token
});
})
.catch(() => {
res.status(401).json({ success: false, message: 'Authentification failed. User not found.' });
});
});
request.query are the parameters passed in the URL, like:
protocol://hostname:port/path/to.route?query_param_0=value_0&query_param_1=value_1
on your express endpoint request.query will be:
{
query_param_0: value_0,
query_param_1: value_1
}
while sending the payload, with the second argument in axios.post(url, payload):
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
on your express endpoint request.body will be:
{
firstName: 'Fred',
lastName: 'Flintstone'
}
when you use app.use(bodyParser.json()); (and you do).
You are using “getDocument(usersBucket, req.query.username)”
This means you express route is expecting username as a request param. That’s why it’s working when you use “?username=xx”
Instead try to get it from json body of request.
“req.body.username”
Also you should consider validating the request body or param as required.
The body of the request being sent is empty according to req.body in my express route.
My main node file is as follows -
var express = require('express');
var bluebird = require('bluebird')
const bodyParser = require('body-parser');
const cors = require('cors');
/*initializations*/
global.mongoose = require('mongoose');
mongoose.Promise = bluebird
global.app = express();
global.config = require('./config/config');
global.jwt = require('jsonwebtoken');
app.use(bodyParser.json({ type: 'application/json' }))
app.use(bodyParser.urlencoded({ extended: true }));//accept strings, arrays and any other type as values
app.disable('x-powered-by');
require('./routes/auth.routes');
//DB connection
app.listen(config.port, function(){
console.log("Express started on " +config.base_url +' in '+config.env +' environment. Press Ctrl + C to terminate');
mongoose.connect(config.db.uri, config.db.options)
.then(()=> { console.log(`Succesfully Connected to the Mongodb Database at URL : `+config.db.uri)})
.catch((error)=> { console.log(error)})
});
The auth.routes file has the signup route and this is where the req.body is empty but it does not hit the if statement that checks, but when i console.log(re.body), it gives me that - {}
app.post('/signup', function(req,res,next){
if (!req.body||req.body=={}){
return res.status(400).send("Bad Request")
}
var user = new User(req.body);
user.password = bcrypt.hashSync(req.body.password, 10);
User.create(user, function(err,new_user){
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.")
}
//success code
})
});
And the request from the angular 4 app is
signup(user:User):Observable<boolean>{
return this.http.post(this.signup_url,JSON.stringify(user),
{
headers: new HttpHeaders().set('Accept', "application/json;q=0.9,*/*;q=0.8").set('Content-Type', "x-www-form-encoded")
})
.map((response: Response) => {
if(response){
if(response.json() && response.json().token&&response.json().user&&response.json().expires){
this.setSession(response.json());
return true;
}
else{
return false;
}
}
else{
return false;
}
});
}
I am certain the Angular 4 app is sending the right data to the server and that its not empty - checked chromes network request body.
I have tried the following links but none worked.
Express app empty request body with custom content type headers
Express receiving empty object
Node.js: Receiving empty body when submitting form.
Also tried with postman and the result is the same - which means the problem is from the express server and not the client side.
There is no need to stringify the posted data, the body-parser middleware will be responsible for parsing the data into object:
return this.http.post(this.signup_url, user, { ... }).map( ... );
One other thing, In the post handler, you might want to use .save() method instead of .create() because you already create a model instance, Remember that the .save() method is available on the model instance, while the .create() is called directly from the Model and takes the object as a first parameter
Example with .save() method:
app.post('/signup', function(req,res,next) {
if (!req.body){
return res.status(400).send("Bad Request");
}
var user = new User(req.body);
var salt = bcrypt.genSaltSync(saltRounds);
user.password = bcrypt.hashSync(req.body.password, salt);
user.save(function( err ) {
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.");
}
//success code
res.json({ success: true });
})
});
Example with .create() method:
router.post('/signup', function(req,res,next){
if (!req.body){
return res.status(400).send("Bad Request")
}
var salt = bcrypt.genSaltSync(saltRounds);
req.body.password = bcrypt.hashSync(req.body.password, salt);
User.create ( req.body, function( err, new_user) {
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.")
}
//success code
res.json({ success: true });
});
});
In my ReactJS project, I am currently running the server with NodeJS and ExpressJS, and connecting to the MongoDB using MongoClient. I have a login API endpoint set up that accepts a request with user's username and password. And if a user is not found, should catch the error and respond with an error (status(500)) to the front-end.
But rather than responding to the front-end with an json error, the server gets crashed. I have tried everything to figure out why but still no luck.
How can I fix the following error? Any guidance or insight would be greatly appreciated, and will upvote and accept the answer.
I intentionally made a request with a username and a password ({ username: 'iopsert', password: 'vser'}) that does not exist in the database.
Here is the login endpoint:
//login endpoint
app.post('/api/login/', function(req, res) {
console.log('Req body in login ', req.body)
console.log('THIS IS WHAT WAS PASSED IN+++++', req._id)
db.collection('users').findOne({username: req.body.username}, function(err, user) {
console.log('User found ')
if(err) {
console.log('THIS IS ERROR RESPONSE')
// Would like to send this json as an error response to the front-end
res.status(500).send({
error: 'This is error response',
success: false,
})
}
if(user.password === req.body.password) {
console.log('Username and password are correct')
res.status(500).send({
username: req.body.username,
success: true,
user: user,
})
} else {
res.status(500).send({
error: 'Credentials are wrong',
success: false,
})
}
})
And here is the terminal error log:
Req body in login { username: 'iopsert', password: 'vset' }
THIS IS WHAT WAS PASSED IN+++++ undefined
User found
/Users/John/practice-project/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
TypeError: Cannot read property 'password' of null
at /Users/John/practice-project/server/server.js:58:12
at handleCallback (/Users/John/practice-project/node_modules/mongodb/lib/utils.js:96:12)
at /Users/John/practice-project/node_modules/mongodb/lib/collection.js:1395:5
at handleCallback (/Users/John/practice-project/node_modules/mongodb/lib/utils.js:96:12)
at /Users/John/practice-project/node_modules/mongodb/lib/cursor.js:675:5
at handleCallback (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:165:5)
at setCursorNotified (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:505:3)
at /Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:578:16
at queryCallback (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:226:18)
at /Users/John/practice-project/node_modules/mongodb-core/lib/connection/pool.js:430:18
And /Users/John/practice-project/node_modules/mongodb/lib/utils.js:98 is referring to the following:
var handleCallback = function(callback, err, value1, value2) {
try {
if(callback == null) return;
if(value2) return callback(err, value1, value2);
return callback(err, value1);
} catch(err) {
process.nextTick(function() { throw err; });
return false;
}
return true;
}
EDIT
Here are everything that's being imported to the server:
"use strict"
var express = require('express');
var path = require('path');
var config = require('../webpack.config.js');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var bodyParser = require('body-parser');
var MongoClient = require('mongodb').MongoClient;
var ObjectId = require('mongodb').ObjectID;
const jwt = require('jsonwebtoken')
var app = express();
var db;
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('dist'));
app.use(bodyParser.json());
And this is how the request is made and error is caught:
loginUser(creds) {
var request = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(creds),
}
fetch(`http://localhost:3000/api/login`, request)
.then(res => res.json())
.then(user => {
console.log(user);
console.log('Successful')
})
.catch(err => {
console.log('Error is', err)
})
},
It looks to me like the error is being thrown on this line because user is not defined.
if(user.password === req.body.password) {...}
Take a harder look at your console statements.
1. Req body in login { username: 'iopsert', password: 'vset' }
2. THIS IS WHAT WAS PASSED IN+++++ undefined
3. User found
4. /Users/John/practice-project/node_modules/mongodb/lib/utils.js:98
5. process.nextTick(function() { throw err; });
^
6. TypeError: Cannot read property 'password' of null
7. at /Users/John/practice-project/server/server.js:58:12
Line 2 shows that req._id is undefined
Your User found statement is printed before you check if there is an error or if the user actually exists, so it isn't representative of there actually being a user.
Line 6 shows that the error is being thrown because you're trying to read a property of password from a null object.
I'd recommend modifying your login logic to look more like this:
//login endpoint
app.post('/api/login/', function(req, res) {
console.log('Performing login with req.body=');
console.log(JSON.stringify(req.body, null, 4));
// check for username
if (!req.body.username) {
return res.status(401).send({message: 'No username'});
}
// find user with username
db.collection('users').findOne({username: req.body.username}, function(err, user) {
// handle error
if(err) {
console.log('Error finding user.');
return res.status(500).send({message: 'Error finding user.'});
}
// check for user
if (!user) {
console.log('No user.');
return res.status(500).send({message: 'No user.'});
}
console.log('User found.');
// check password
if(user.password !== req.body.password) {
console.log('Wrong password.');
return res.status(401).send({message: 'Wrong password.'});
}
// return user info
return res.status(200).send(user);
});
Some final thoughts:
Make sure to handle the error (if it exists) and check that user exists before proceeding.
Always include return in your return res.status(...).send(...) statements, otherwise the subsequent code will execute.
It's generally not a good idea to save passwords as simple strings. Work toward encrypting them. Look at passport or bcrypt.
Hope this helps.