I'm building an API that I intend to use with my react native application. The problem which I'm facing right now is that I when I try to hit this particular route /api/auth/signup in Postman I get the Could not get any response error message.
This is the route:
//create user token
router.post(
"/signup",
[check("username").isEmail(), check("password").isLength({ min: 6 })],
async (req, res) => {
//validate input field on the backend
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
const { username, password, firstName, lastName } = req.body;
try {
//search DB if there is an existing user
let user = await User.findOne({ username });
if (user) {
return res.status(400).json({ msg: "User already exists" });
}
const salt = await bcrypt.genSalt(10);
res.status(200).json({ data: salt });
} catch (error) {}
}
);
module.exports = router;
The strange thing is that if I remove the User.findOne function I get a response. I don't know why this keeps happening, as I had built a similar application following the same pattern without a problem.
NOTE: In the main app.js I have the app.use(express.json({extended:true}), I've also successfully linked the routes in the main file too. Any help would be greatly appreciated!
I think you have a typo in your app.use... statement which you've given in your comment as,
app.use(express.json({extented:true})
which needs to be corrected as, ('d' should come in place of 't')
app.use(express.json({extended:true})
Hope this helps!
So it turns out when I was requiring mongoose in one of my models I required it with an uppercase letter const mongoose = require('Mongoose') which isn't valid, therefore the findOne method won't run because there's no model to take it from. Strange that visual studio code didn't complain about the false import. Anyways, thanks to anyone willing to help me out :) !
Related
I am trying to create a project which allows the user to enter "email", "username" and "Password" to register to the site,
When I try to enter a user using the "username", email" and "password" to enter this site, I get the following error:
Backend server is running
not connected
C:\Users\odewo\chat-app\NODE-REST-API\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:149
const err = new MongooseError(message);
^
MongooseError: Operation `users.insertOne()` buffering timed out after 10000ms
at Timeout.<anonymous> (C:\Users\odewo\chat-app\NODE-REST-API\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:149:23)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
Below is my mongoose code:
mongoose.connect(process.env.MONGO_URL, {
userNewUrlPaser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => {
console.log('Connected to MongoDB');
})
.catch((e) => {
console.log('not connected');
});
This is auth.js code for routes:
const router = require('express').Router();
const User = require('../models/User');
// REGISTER
router.get('/register', async (req, res) => {
const user = await new User({
username: 'samson',
email: 'samson#gmail.com',
password: '123456',
});
await user.save();
res.send('ok');
});
module.exports = router;
I will really appreciate your help
Monogo Db Atlas -> Network Access -> IP Access List -> IP address updated to access from any where works for me.
Reason : My Internet provider allots dynamic IP and every time i login the IP Address changes and this throws error.
Check your MONGO_URL and make sure your password or username is not wrapped in < > tags. I think your code is fine but its obvious that there are some problems. I would be checking the .env file to make sure.
The problem is not solved by removing the await. It is better to implement this function asynchronously. You must log in to your mongo account and add your IP address from the Network Access part.
you should remove the await before the new User declaration , use await just when you are waiting for the result of a promise like in await user.save()
You can solve this problem by using a simple password for your atlasDatabase meaning avoid using any special characters in your password like "#" or "$" and if you use then they must be url encoded that's why it is better if you avoid special characters and it worked for me and I hope it will work for you too.enter image description here
Below code is my fetch method in my separate register.js. This is my newly created js file, so I can create my front end. At the moment I'm just trying to console.log the ending result for this fetch, but can't get the output since I'm getting an error when I try to POST this.
error in browser console:
"Uncaught (in promise) SyntaxError: Unexpected token S in JSON at position 0"
fetch(`${rootUrl}api/users/register`,{
method:"POST",
headers:{
"Content-Type": "application/json"
},
body:JSON.stringify({
firstName: firstName,
lastName: lastName,
mobileNo: mobileNo,
email: email,
password: password
})
})
.then(result=>result.json())
.then(result =>{
console.log(result);
})
In userRouter.js, this is the route I'm fetching in register.js above:
router.post('/register', (req, res)=>{
userController.register(req.body).then(result => res.send(result))})
And the route leads to this controller in Usercontroller.js:
module.exports.register = (reqBody)=>{
//check if email already exists before registering new user
return User.find({email: reqBody.email}).then((result, error) =>{
if(result.length != 0){
return "EMAIL EXISTS!";
}else{
let newUser = new User({
firstName: reqBody.firstName,
lastName: reqBody.lastName,
email:reqBody.email,
password: bcrypt.hashSync(reqBody.password, 10),
mobileNo: reqBody.mobileNo
})
return newUser.save().then((result, error)=>{
if (error){
return error;
}else{
return "SUCCESFULLY REGISTERED NEW USER";
}
})
}
})}
As you can see, this is a registration form. Everything works fine in the backend, using postman to enter values. All my condition prompts are being returned(emails exist, successful registration).
But when I tried creating a frontend for it, I can't get my defined prompts.Like when I deliberately input a duplicate email, I can't get the message "Email exists" that I used to get when using only postman or just backend API functionality.
I feel like something is very wrong with what I'm trying to do. I'm having trouble creating a frontend for my API which I'm not used at the moment.
You are returning a non JSON response, so you can't use res.json(). You are simply sending a text response. So use res.text()
fetch('/your-endpoint').then((res)=>{
return res.text();
}).then((text)=>{
console.log(text)
})
There are some hints to be pointed out.
Check the rootUrl since after that there's no foreslash.
You're sending back the Text Style. use this instead to have the result json format.
res.status(200).json({result})
Try not to use Synchronous Functionalities on the back side. You
should give it a wide berth, or rather, use bcrypt.hash() mixed
with Async Function to remove the CallBack Function until it gets
done. What's more, so as to check the previous function's error to
get it on the front side using Fetch / Axios. use Try/Catch method.
I'd use axios [ axios.request({}) | axios.post(..)] if I were in
your shoes.
Hi fellow developers!
I've got this error showing up in my console when I try to save two identical documents in a collection in MongoDB that has nothing to do with the index shown in the error.
Here's the error: E11000 duplicate key error collection: Bohemian.orders index: user.email_1 dup key: { user.email: null }
Now this makes no sense, because I'm trying to save an Order document in a separate collection, which has nothing to do with the user router I had set up previously.
Here is the schema and model code:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
amountToPay: Number,
});
const Order = mongoose.model("Order", orderSchema);
module.exports.Order = Order;
As shown here, I am only trying to save the amount to be payed into the database in a separate collection.
Here is the router file:
const express = require('express');
const router = express.Router();
const { Order } = require('../models/Order');
router.get("/", async (req, res, next) => {
try {
const orders = await Order.find();
if (orders.length === 0) return res.status(404).send("There are currently no orders");
res.send(orders);
} catch (ex) {
console.error(ex);
next();
}
});
router.post("/", async (req, res, next) => {
try {
const order = new Order({
amountToPay: req.body.amountToPay
});
await order.save();
res.send(order);
} catch (ex) {
console.error(ex);
next();
}
});
module.exports = router;
As you can see there is nothing relative to the error that I'm getting and I have no clue why I'm getting a duplicate user.email = null key , when I haven't made any reference to the User model or router.
Here is the POST call I'm making from POSTMAN to test:
Pretty straight forward, nothing extreme, nothing tangled, right? Well the first ever POST call saves the document in the Database, but from then on I keep getting the same error. The only thing I can take from that is that when I save the first document, Mongo looks for the user.email property when I'm creating the new instance of Order and when it doesnt find it, it creates it with a value of null and then the next document would naturally be a duplicate, hence the error. But I'm confused, because this model and router should not absolutely nothing to do with the user ones.
Here is the error:
So please if anyone can help me understand why MongoDB is screwing with me or where I'm making a mistake, I would really appreciate it.
I found out what the problems was, thanks to Molda:
At some point I had created these indexes, which I'm still unsure when and how, but I did. Which essentially lead to this error, when I tried to save the second document.
A simple quick console log of the indexes in the collection showed that I had that index in there.
const indexes = await Order.collection.getIndexes();
console.log(indexes);
Then I removed them using this method:
await Order.collection.dropIndexes("user.email_1");
And everything worked flawlessly from there.
I hope this helps anyone in this situation in the future and thanks Molda! :)
I can register just fine, however when I get directed to my game route I get a default Error page that's just white with [object Object] on the screen. Then I get the same in my console, [object Object] and it repeats every once in a while.
At first I thought it was something to do with socket.io, but it isn't even getting to that point. I think it might be something with passport and how I have it configured, not being setup good with the promise route I'm going, but I am at a complete loss. I don't know exactly where this error is occurring.
Here is the passport file:
/*jshint esversion: 6 */
const LocalStrategy = require('passport-local').Strategy;
const db = require('../config/db');
const bcrypt = require('bcryptjs');
let io = require('./io');
module.exports = (passport) => {
// Local Strategy login
passport.use('local-login',
new LocalStrategy((username, password, done) => {
console.log('username');
// Match Username
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username]).then(results => {
if (!results.length) {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
console.log('password');
// Match Password
bcrypt.compare(password, results[0].password, (err, isMatch) => {
if (isMatch) {
console.log('Password is correct');
return done(null, results[0]);
} else {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
});
});
}));
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// used to serialize the user for the session
passport.serializeUser((user, done) => {
console.log(user.username + ' has been Serialized');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
db.query('SELECT * FROM users WHERE id = ?', [id]).then(results => {
console.log(results[0].username + ' has been deserialized');
done(results[0]);
});
});
};
This seems to go off without a hitch, now here is my login redirect:
// Login Process
router.post('/login',
passport.authenticate('local-login', {
successRedirect: '/game',
failureRedirect: '/',
failureFlash: true,
}), (req, res) => {
console.log('login route test');
});
Again this seems to be doing well, it does in fact redirect me as intended. Now, here is some extra stuff I think might be causing it:
// Passport config
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
app.get('*', function (req, res, next) {
res.locals.user = req.user || null;
next();
});
Then the game route:
//Route to game app
app.get('/game', function (req, res) {
console.log('Log before it checks req.user');
if (req.user) {
console.log('req.user is working');
res.render('game');
} else {
req.flash('error', 'You need to be signed in!');
res.redirect('/');
}
});
So here is the thing here: When I am not logged in and go to the /game route it will kick me back into my main route with the correct flash error. However, when I login, I can;t for the life of me get it to fire off a console.log() function. So I am thinking it is getting stuck with the req.user on login, but I am not sure why nor how. If more information is needed, I can give more... but this is mostly what all handles the login process (except socket.io, but it doesn't even get to that point yet, and all my socket.io file does is send the data client side for easy updates).
I will keep trying my luck, but since I am new to promises, this may be the reason why, and if it is, I may not be so lucky.
EDIT: Well, I've changed everything back to a normal callback for my DB (which is what I had it before). Weirdly though, I am getting the same result, and I've no idea why. This project had been put on hold for months, but I hadn't touched anything until I changed all the DB stuff. So something must have broken before I even touched anything when I left this project it was working just fine. But I did change it back to the promise method, because I'd rather stick to this message.
EDIT: Also, I am getting a 500 internal server error on the browser console.
EDIT: Updated code and added console.logs in more places to see where this is hanging up, and I'm still not sure. So here is the logging sequence when I click the login button:
username
password
Password is correct
Bloodmorphed has been Serialized
Bloodmorphed has been deserialized
[object Object]
Bloodmorphed has been deserialized
[object Object]
EDIT: So it seems like the login process is not working correctly. I am not sure why and I can't find a problem with anything I am doing. I have looked at multiple sources of how to set-up passport for MySQL and while some of them differ a tiny bit, they all seem to be the same where it matters. I do not know why this is happening and according to multiple sources of working logins, I am doing this right.
I am, well... simply an idiot. I forgot when I changed to a promise system, I handle errors inside the query itself, so there was no reason for me to pass it through.
so where I had done done(results[0]) in the passport.deserializeUser... I just had to add null before it like so: done(null, results[0]) so much wasted time on a simple matter. I feel like a dumby.
I am currently trying to test my node api w/ mocha chai. I am running into a scenario where a test should actually fail but is passing. I have a repo up of the current API that I am building here if you want to play around with it: enter link description here. However, I am still going to walk through the code in this question.
I'm trying to test the controller with the following code:
import chai, { expect } from 'chai';
import chaiHttp from 'chai-http';
import server from '../../src/app';
chai.use(chaiHttp);
describe('Authentication Controller', () => {
const testUser = {
email_address: 'test#test.com',
password: 'test'
};
describe('login success', () => {
it('responds with status 200', done => {
chai.request(server)
.post('/api/auth/login')
.send(testUser)
.end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
});
describe('login failure', () => {
it('responds with status 401', done => {
chai.request(server)
.post('/api/auth/login')
.send(testUser.email_address = 'fake#news.com')
.end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
});
});
Obviously I want to test a successful login and a failed login attempt. However, both the response statuses from the server are 200 and this should not be the case. When testing in Postman the response status when an individual tries to login with an email address that doesn't exist or a password that doesn't match, it returns a status of 401. If I write a test
expect(1).to.equal(1) => test passes.
expect(1).to.equal(2) => test fails.
Here is the controller function that handles the request for logging in:
export function login(req, res) {
User.findOne({email: req.body.email})
.then(user => {
if (user && bcrypt.compareSync(req.body.password, user.password)) {
generateToken(res, user);
} else {
res.status(401).json({
success: false,
message: 'Incorrect username or password.'
});
}
})
.catch(err => {
res.json(err);
});
}
The model that handles the request:
export function createUser(req) {
return db('users').insert(Object.assign(req.body,{password: hashPassword(req.body.password)}))
.then((id) => db('users').select().where('id', id).first());
}
As you can see I am using Knex.js. I have setup a test database and everything is connected appropriately, so I'm confused as to why my server is responding w/ a 200 response status when testing?
I just want to say thanks to anyone who takes the time to help me understand how mocha chai is working. I have very LITTLE experience with testing applications, but I want to start familiarizing myself w/ doing so because I believe it to be good practice.
I actually cloned your Github repo and tried running the test. From what I have seen, there are a couple of different issues in your code, as followed:
1. from the controller function you posted in the question:
```
export function login(req, res) {
User.findOne({email: req.body.email})
.then(user => {
// [removed because of little relevancy]
})
.catch(err => {
res.json(err);
});
}
```
The issue is the line res.json(err) which actually responded with a 200 status code (even though it was an error in this case). This is because res.json does not automatically set the HTTP response status code for you when you "send an error object". This fooled the client (in this case, chai-http) into thinking it was a successful request. To properly respond with an error status code, you may use this instead: res.status(500).json(err)
It's also worth noticing that some of your other controller functions got into this issue too.
2. from your userModels.js file, line 10, which is:
```
return db('users').select().where(req).first();
```
You are using Knex API in an incorrect way. It should be ...where('email', req.email)... This was the initial reason why your requests failed.
3. you set up your unit tests in different manners:
Test no. 1 (login success):
```
chai.request(server)
.post('/api/auth/login')
.send(testUser)
```
Test no. 2 (login failure):
```
chai.request(server)
.post('/api/auth/login')
.send(testUser.email_address = 'fake#news.com')
```
So, what happened?
In the first test, you passed an object into .send(), whereas in the second test, you simply passed an expression. When done this way, the model handler, userModels.findOne(), received an object with keys email_address and password for the first test, but for the second test, it did not.
Also, in your 1st test case, you sent testUser.email_address, but in your controller function, you referenced req.body.email.
All these, in addition to the issue no. 1 as I mentioned earlier, further complicated your test suite, leading to your misunderstanding in the end.
Disclaimer:
All what I wrote above was based on the source code from your Github repo, so if you have fixed some issues since you pushed your code, and some (or all) of my points above are no longer valid, please disregard. Nevertheless, I wish you have found, or will soon find out why your code didn't behave as you expected!
Cheers,
I just wanted to post an answer here that is specific to my experience and what helped me get all of this setup. The only thing that I really needed to change on the Repo Proj was the property email on the user object I was passing. I had email_address and was thus searching for that column in the database whilst it did not exist! So once I changed that I started down the right path.
I was then able to get my failed login test to pass. However, my successful login didn't pass. The reason was because I was seeding my database with a plain string password. Thus, when I performed the conditional statement of:
if (user && bcrypt.compareSync(req.body.password, user.password))
It wasn't passing because the bcrypt.comparSync was looking for a password that was hashed. In order to get this to work I needed to require babel-register in my knex file. This then allowed me to use es6 and perform my hashPassword function:
test/userSeed.js
import hashPassword from '../../src/helpers/hashPassword';
exports.seed = function(knex, Promise) {
return knex('users').truncate()
.then(() => {
return knex('users')
.then(() => {
return Promise.all([
// Insert seed entries
knex('users').insert([
{
first_name: 'admin',
last_name: 'admin',
email: 'admin#admin.com',
password: hashPassword('test')
},
{
first_name: 'test',
last_name: 'test',
email: 'test#test.com',
password: hashPassword('test')
}
]),
]);
});
})
};
hashPassword.js
import bcrypt from 'bcrypt';
export default function(password) {
const saltRounds = 10;
let salt = bcrypt.genSaltSync(saltRounds);
return bcrypt.hashSync(password, salt);
}
This resulted in the hashing of my users password when I seeded the DB. Tests all pass as they should and api works as intended using Postman.