http post inside .then() - node.js

I want to do a http post inside .then(). I've already it in many diferent ways...nothing worked
I'm trying to create an user and do an http POST after the creation.
'use strict';
module.exports = function(app) {
return function(req, res, next) {
const body = req.body;
// Get the user service and `create` a new user
app.service('users').create({
email: body.email,
password: body.password
}).then('I WANT HTTP POST WITH PARAMS HERE')
// On errors, just call our error middleware
.catch(next);
};
};
I want to send email and password in the POST

You can return a Promise in promise chain. I would use promisified request to do a postAsync here.
var Promise = require('bluebird')
var request = Promise.promisifyAll(require('request'))
app.service('users').create({
email: body.email,
password: body.password
}).then(function(createUserResp) {
return request.postAsync(/**/)
})
}).then(function(resp) {
// do sth
})
.catch(next);

var RP = require('request-promise')
app.service('users').create({
email: body.email,
password: body.password
}).then(function () {
var opt = {
url: 'targetUrl',
formData: {
email: body.email,
password: body.password
//, json: true // if result is in json format
}
}
return RP.post(opt)
}).then(function (postResult) {
})
.catch(next);

Related

Router doesn't get response from database functions

when running the app and testing the /signup route the data gets written to the db and i can console.log it from the database/models.js file, but in the routes/index.js file it returns "Something went wrong." and postman shows nothing, not even an empty array or object.
routes/index.js
var express = require('express');
var router = express.Router();
const database = require('../database/models');
router.post('/signup', function(req, res, next) {
if (!req.body.email || !isValidEmail(req.body.email))
res.status(400).send('Email invalid.');
else if (!req.body.username || !isValidCredential(req.body.username) || !req.body.password || !isValidCredential(req.body.password))
res.status(400).send('Username/password invalid.');
else {
const result = database.createUser(req.body.email, req.body.username, req.body.password);
if (result)
res.status(200).send(result);
else
res.send('Something went wrong.');
}
});
function isValidEmail (email) {
if (email.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"))
return true;
else
return false;
}
function isValidCredential (credential) {
if (credential.length < 6)
return false;
else if (credential.match(/^[a-z0-9]+$/i))
return true;
else
return false;
}
module.exports = router;
database/models.js
const tools = require('./tools');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: String,
username: String,
hashedPassword: String,
salt: String,
accessToken: { type: String, default: "" }
});
const User = mongoose.model('User', userSchema);
function createUser(email, username, password) {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
newUser.save(function (error, result) {
if (error)
return handleError(error);
return { email: result.email, username: result.username };
});
}
module.exports.createUser = createUser;
In your code you are not returning anything when calling the createUser function. Here a couple of considerations:
// index.js
const result = database.createUser(req.body.email, req.body.username, req.body.password);
since the createUser is an operation performed on a database, it will be probably asynchronous, and therefore also its result. I suggest the usage of async/await to be sure of the returned result. Also, you need to change the code of your models.js file to return a Promise and await for it.
function createUser(email, username, password) {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
return new Promise((resolve, reject)=> {
newUser.save(function (error, result) {
if (error) reject(error);
resolve({ email: result.email, username: result.username });
});
});
}
and than you will have to await for your result. You can do it in the following way:
// index.js
// Add async here
router.post('/signup', async function(req, res, next) {
// ...other code
// Add await here
const result = await database.createUser(req.body.email, req.body.username, req.body.password);
I figured it out.
in routes/index.js use async/await like this:
router.post('/signup', async function(req, res, next) {
try {
if (!req.body.email || !isValidEmail(req.body.email))
res.status(400).send('Email invalid.');
else if (!req.body.username || !isValidCredential(req.body.username) || !req.body.password || !isValidCredential(req.body.password))
res.status(400).send('Username/password invalid.');
else {
const result = await database.createUser(req.body.email, req.body.username, req.body.password);
if (result)
res.status(200).send(result);
else
res.status(403).send(result);
}
} catch (error) { return error; }
});
and in database/models.js use async/await as well, but also rewrite mongoose methods into ones without callbacks, with returns into variables, like this:
async function createUser(email, username, password) {
try {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
const result = await newUser.save();
return { email: result.email, username: result.username };
} catch (error) { console.log (error); return error; }
}

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

How to handle login with MongoDB through REST API?

I'm not sure how to check if the values match with the MongoDB data. I am using PUT and trying to use findOneAndUpdate to check if the values match.
<script>
const logindetails = new Vue({
el: '#logindetails',
data: {
email: "",
password: "",
on: Boolean
},
methods: {
login: function (e) {
e.preventDefault();
const log = {
email: this.email,
password: this.password,
}
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(log)
};
fetch('http://localhost:3000/users/${this.email}/${this.password}',
options).then(response => {
[...]
</script>
This is the server code (it successfully connected to MongoDB) :
app.put('/students/:email/:password', (req, res, next) => {
console.log("login");
res.setHeader("Content-Type", "application/json");
db.collection('users').findOne({email: (req.params.email), password: (req.params.password)},
{$set: {on: true}})
.then(results => res.send(results))
.catch(err => res.send(err))
});
I personally don't think it is a good idea to put your username and password as query string, because it hurts the restful api convention. It wouldn't make sense to use a put request if there is no body being pass. Also, a post request would make more sense in a login situation .Anyway I digress, here are the usual steps to doing authentication.
1. (Client-Side) Send the email and password in the body of the fetch request
//something like this
const body = { email, password };
const response = await fetch(
"http://localhost:5000/authentication/login",
{
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(body)
}
);
2.(Server-Side - make sure you to use app.use(express.json()) to access req.body)
//defining middleware to access req.body
app.use(express.json());
app.post("/authentication/login", async(req,res) =>{
//1. destructure email and password
const {email, password} = req.body
//2. check if user doesn't exist
const user = await db.user.find({user_email: email})
if(!user){
return res.status(401).send("User does not exist");
}
//3. Check if password is the same as the password in the database
if(password !== user.password){
return res.status(401).send("Wrong Credential")
}
//4. This is up to you when the user is authenticated
res.json(`Welcome back ${email}`);
})

Route.post() requires a callback function but got a [object Object]

I am chaining custom middleware function for my route handler in express but I am getting the above(title) error . Why is that?
Here is my code for middleware:
const Joi = require("joi");
function validateCredentials(req, res, next) {
const schema = {
email: Joi.string()
.max(1024)
.required()
.regex(/^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/),
password: Joi.string()
.min(6)
.max(255)
.required()
};
const result = Joi.validate({ email: req.body.email, password: req.body.password }, schema);
if(!result.error) {
return next();
}
}
module.exports.validateCredentials = validateCredentials ;
Here is route handler:
router.post('/api/signup', validateCredentials, passport.authenticate('local-signup'), (req, res) => {
const response = {};
response._id = req.user._id;
response.email = req.user.local.email;
res.send(response);
});
You are calling next only when validation passes not when there is an error.
Did you try this?
if (!result.error) {
return next();
} else {
return next(result.error);
}

Mocha route testing not executing asynchronously

I have started working with mocha and I have a problem with one particular test case. Here is the code:
var assert = require("chai").assert;
var request = require('supertest');
var http = require("http");
var conf = require("../config/config");
var app = require("../app");
var mongoose = require('mongoose');
var User = mongoose.model('User');
describe('User controller', function(){
describe('POST /register', function(){
it('should return false when the parameters are not unique', function (done) {
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:true, redirect:'/user/registerConfirmation'})
.end(function(err, res) {
if (err) {
return done(err);
}
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:false}, done);
});
});
});
I am expecting the result to be false because after the insertion of a user inside the database, the unique index rule should raise an error. When I run this test, I get this: {success: true, redirect: '/user/registerConfirmation'} , I should be getting this: {success: false}. I noticed that when I don't clear the database before each tests (in utils.js) I am getting the expected value. Do I get this error because of an asynchronous error? How can I rewrite this test to make sure that it works?
Thanks
Files
util.js contains the configurations for the test sequence:
'use strict';
process.env.NODE_ENV = 'test';
var config = require('../config/config');
var mongoose = require('mongoose');
beforeEach(function (done) {
mongoose.connection.db.dropDatabase();
return done();
});
afterEach(function (done) {
return done();
});
user.js the user model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var UserSchema = new Schema({
username: {type: String, required: true, unique: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
status: {type: Number, default:0}
});
UserSchema.virtual('date')
.get(function(){
return this._id.getTimestamp();
});
UserSchema.pre('save', function(next) {
//Password encryption ...
});
mongoose.model('User', UserSchema);
user.js (controller) is the controller for all the user routes.
...
router.post('/register', function (req,res,next){
var newUser = new User({
username: req.body.username
, email: req.body.email
, password: req.body.password
});
newUser.save(function(err){
if(err){
res.send({success: false});
}else{
var newToken = new UserToken({
userId: newUser._id
, email: newUser.email
});
newToken.save(function(err){
if(err){
res.send({success: false});
}else{
res.send({success: true, redirect: '/user/registerConfirmation'});
}
});
}
});
});
...
Edit
I have tried the end() function and it still doesn't work.
There is a problem in how you chain supertest requests in your test test suite - the second request is not invoked properly. When you were not clearing the database the test was failing on the first .expect({success: true, ...}) and you were getting the expected value.
The correct way is to perform the first request with .end method, check for potential errors, and then perform the second request to see if it has failed:
describe('User controller', function(){
describe('POST /register', function(){
it('should return false when the parameters are not unique', function (done) {
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:true, redirect:'/user/registerConfirmation'})
.end(function(err, res) {
// Check if first request has failed (it should not!)
if (err) {
return done(err);
}
// Testing the second, not unique request. that should fail
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:false}, done);
});
});
});
In mocha, the done() function should be called when your async call is done - regardless of whether an error is hit. Also, use the .done() function - which is part of the promise API - to end the promise chain and ensure any exceptions are caught. Without the done function, you can miss runtime errors completely, as they are suppressed by the promise library.

Resources