I am creating a RESTful API using Node.js , express and My sql. I have created 2 Services,2Routers, and 2 Controllers
App.js module
require("dotenv").config();
const express = require("express");
const app = express();
const userRouter = require("./api/Routers/user.router");
const categoryRouter=require("./api/Routers/category.router");
app.use(express.json());
app.use("/api/users", userRouter);
app.use("/api/category", categoryRouter);
const port = process.env.PORT ;
app.listen(port, () => {
console.log("server up and running on PORT :", port);
});
Category.service.js
const pool = require("../../config/database");
module.exports = {
create: (data, callBack) => {
pool.query(
`insert into category(name, , branch, state, create_date, modified_date)
values(?,?,?,?,?,?)`,
[
data.name,
data.branch,
data.state,
data.create_date,
data.modified_date,
],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results);
}
);
},
getUserByUserEmail: (email, callBack) => {
pool.query(
`select * from registration where email = ?`,
[email],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
getCategoryByCategoryId: (id, callBack) => {
pool.query(
`select id,name,branch,state,create_date,modified_date from category where id = ?`,
[id],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
getCategory: callBack => {
pool.query(
`select id,name,branch,state,create_date,modified_date from category`,
[],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results);
}
);
},
updateCategory: (data, callBack) => {
pool.query(
`update category set name=?, branch=?, state=?, create_date=?, modified_date=?, `,
[
data.name,
data.branch,
data.state,
data.create_date,
data.modified_date,
],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
deleteCategory: (data, callBack) => {
pool.query(
`delete from category where id = ?`,
[data.id],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
}
};
Category.route.js
const router = require("express").Router();
const { checkToken } = require("../../auth/token_validation");
const {
createCategory,
login,
getCreateByCreateId,
getCategory,
updateCategory,
deleteCategory
} = require("../Controllers/category.controller");
router.get("/", checkToken, getCategory);
router.post("/", checkToken, createCategory);
router.get("/:id", checkToken, getCreateByCreateId);
router.post("/login", login);
router.patch("/", checkToken, updateCategory);
router.delete("/", checkToken, deleteCategory);
module.exports = router;
Category.controll.js
const {
create,
getUserByUserEmail,
getCategoryByCategoryId,
getCategory,
updateCategory,
deleteCategory
} = require("../services/category.service");
const { hashSync, genSaltSync, compareSync } = require("bcrypt");
const { sign } = require("jsonwebtoken");
module.exports = {
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
body.password = hashSync(body.password, salt);
create(body, (err, results) => {
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection error"
});
}
return res.status(200).json({
success: 1,
data: results
});
});
},
login: (req, res) => {
const body = req.body;
getUserByUserEmail(body.email, (err, results) => {
if (err) {
console.log(err);
}
if (!results) {
return res.json({
success: 0,
data: "Invalid email or password"
});
}
const result = compareSync(body.password, results.password);
if (result) {
results.password = undefined;
const jsontoken = sign({ result: results }, process.env.JWT_KEY, {
expiresIn: "1h"
});
return res.json({
success: 1,
message: "login successfully",
token: jsontoken
});
} else {
return res.json({
success: 0,
data: "Invalid email or password"
});
}
});
},
getCategoryByCategoryId: (req, res) => {
const id = req.params.id;
getCategoryByCategoryId(id, (err, results) => {
if (err) {
console.log(err);
return;
}
if (!results) {
return res.json({
success: 0,
message: "Record not Found"
});
}
results.password = undefined;
return res.json({
success: 1,
data: results
});
});
},
getCategory: (req, res) => {
getCategory((err, results) => {
if (err) {
console.log(err);
return;
}
return res.json({
success: 1,
data: results
});
});
},
updateCategory: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
body.password = hashSync(body.password, salt);
updateCategory(body, (err, results) => {
if (err) {
console.log(err);
return;
}
return res.json({
success: 1,
message: "updated successfully"
});
});
},
deleteCategory: (req, res) => {
const data = req.body;
deleteCategory(data, (err, results) => {
console.log(results);
if (err) {
return;
}
if (!results) {
return res.json({
success: 0,
message: "Record Not Found",
h:results
});
}
return res.json({
success: 1,
message: "category deleted not successful"
});
});
}
};
I am getting Error: Route.post() requires a callback function but got a [object Undefined. how can i sort it out
Error: Route.post() requires a callback function but got a [object Undefined] how can I sort this error
createUser should be renamed in createCategory in Category.controll.js
Related
My basic POST API makes a call to database , first to authenticate user and then to authentic if the products provided are true. But before productValidation can complete, res.end() is being executed.
As output, I get ::
"validated object"
"stopping the res"
"validated object"
Whereas I am trying to validated and add each cartItem to database and only then send a success response to client.
const express = require("express");
const app = express();
const router = express.Router();
var connection = require("./../database/serverConnector");
router.get("/:userId", (req, res, next) => {
connection.query(
"SELECT * FROM cart_items AS c INNER JOIN product AS p ON c.product_id = p.id WHERE user_id = ?",
[req.params.userId],
function (error, results, fields) {
if (error) throw error;
else {
console.log("Query made for cart of user " + req.params.userId);
res.status(200).send(results);
}
}
);
});
router.post("/:userId", userValidate, (req, res, next) => {
multipleProductValidation(req, res, next).then(function (data) {
res.end();
console.log("stopping the res");
});
// .catch(error){
// console.log(error);
// }
});
function userValidate(req, res, next) {
connection.query(
"SELECT * from user where id = ?",
[req.params.userId],
function (error, user, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
if (user.length == 0) {
res.status(404).json({
message: "User not found with ID " + req.params.userId,
});
} else next();
}
}
);
}
function multipleProductValidation(req, res, next) {
return new Promise(function (resolve, reject) {
req.body.cartItems.forEach((item, i) => {
productValidate(item, req, res, next).then(function (data) {
resolve(data);
console.log("validated object");
//next();
});
});
});
}
function productValidate(item, req, res, next) {
return new Promise(function (resolve, reject) {
connection.query(
"SELECT * from product where id = ?",
[item.product_id],
function (error, product, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
if (product.length == 0) {
res.status(404).json({
message: "Product not found with ID " + item.product_id,
});
resolve(false);
} else {
connection.query(
"INSERT INTO cart_items (product_id,user_id,size,quantity) VALUES (?,?,?,?)",
[item.product_id, req.params.userId, item.size, item.quantity],
function (error, results, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
res.statusText =
"Items added to cart for User " + req.params.userId;
resolve(true);
}
}
);
}
}
}
);
});
}
module.exports = router;
So what is happening here is that you are calling resolve in your multipleProductValidation before you finish processing all the products.
something like this should fix your problem
function multipleProductValidation(req, res, next){
return new Promise(function(resolve, reject) {
let promiseArr = req.body.cartItems.map(item => productValidate(item, req, res, next))
Promise.all(promiseArr).then(values => resolve(values))
}
)}
Im trying to connect to an api I made with node.js but it looks like there isn't a clear way to do it if there requires a body in the request. Im able to connect to it with postman on localhost because there i can set the params in the body but when i use the heroku url it throws an error. I read that apple made it so swift can no longer make requests with a body anymore. In another project i was able to successfully connect to an api made with python but instead of passing the data to the body it was passed as params. How can I change the api/swift code to accept the data as params in swift instead of the body?
Heres the function call in swift:
func logoin() {
let params: Parameters = [
"email" : "test#email.com",
"password" : "1234"
]
AF.request("https://app_name.herokuapp.com/user/login", method: .post, parameters: params, encoding: JSONEncoding.default, headers: ["Content-Type": "application/json"]).responseJSON { response in
if let error = response.error {
print(error)
}
else{
let jsonData = try! JSON(data: response.data!)
print("json data", response)
}
}
}
Heres the api in node.js
const User = require('../models/user')
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
exports.get_user = (req, res, next) => {
const id = req.params.userId;
User.findById(id)
.exec()
.then(doc => {
console.log('From database', doc);
if (doc) {
res.status(200).json(doc);
}
else {
res.status(404).json({ message: 'No valid entry found for user ID.' });
}
})
.catch(err => {
console.log(err);
res.status(500).json({ error: err });
});
};
exports.user_login = (req, res, next) => {
User.find({ email: req.body.email }).exec().then(users => {
if (users.length < 1) {
return res.status(401).json({
message: 'Auth failed1'
});
}
bcrypt.compare(req.body.password, users[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed2'
});
}
if (result) {
const token = jwt.sign({
email: users[0].email,
userId: users[0]._id
}, process.env.JWT_KEY)
return res.status(200).json({
message: 'Auth successful',
token: token
});
}
res.status(401).json({
message: 'Auth failed3'
});
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.delete_user = (req, res, next) => {
User.deleteOne({ _id: req.params.userId })
.exec()
.then(result => {
res.status(200).json({
message: 'User deleted'
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.update_user = (req, res, next) => {
const id = req.params.userId;
const updateOps = {};
for (const ops of req.body) {
updateOps[ops.propName] = ops.value;
}
User.updateOne({ _id: id }, { $set: updateOps })
.exec()
.then(result => {
console.log(result);
res.status(200).json(result);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.signup = (req, res, next) => {
User.find({ email: req.body.email }).exec().then(user => {
if (user.length >= 1) {
return res.status(409).json({
message: 'User already exists'
});
}
else {
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).json({
error: err
});
}
else {
const user = new User({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: hash
});
user
.save()
.then(result => {
console.log(result)
res.status(201).json({
message: 'Successfully created user'
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
})
}
})
}
}).catch();
};
I am finding it hard to convert this user controllers code to async await. Can someone please help and guide me how can i do it too. So that i can also change any callbacks into async await.
Also if someone can provide a good source so that i can read about async await and how to apply them properly.
const User = require("../models/user")
exports.getUserById = (req, res, next, id) => {
User.findById(id).exec((error, user) => {
if (error || !user) {
return res.status(400).json({
error: "No user was found in DB"
})
}
req.profile = user
next()
})
}
exports.getUser = (req, res) => {
req.profile.salt = undefined;
req.profile.encrypted_password = undefined;
return res.json(req.profile)
}
exports.getAllUsers = (req, res) => {
User.find().exec((error, users) => {
if (error || !users) {
return res.status(400).json({
error: "No users was found in DB"
})
}
return res.json(users)
})
}
exports.updateUser = (req, res) => {
User.findByIdAndUpdate(
{ _id: req.profile._id },
{ $set: req.body },
{ new: true, useFindAndModify: false },
(error, user) => {
if (error) {
return res.status(400).json({
error: "You are not authorized to update this info"
})
}
user.salt = undefined;
user.encrypted_password = undefined;
res.json(user)
}
)
}
It should look something like this:
const User = require("../models/user");
exports.getUserById = async (req, res, next, id) => {
let user = await User.findById(id);
try {
if (!user) {
return res.status(404).json({
error: "No user was found in DB"
});
}
req.profile = user;
next();
} catch (err) {
return res.status(500).json({
error: "Something went wrong"
});
}
};
exports.getUser = (req, res) => {
req.profile.salt = undefined;
req.profile.encrypted_password = undefined;
return res.json(req.profile);
};
exports.getAllUsers = async (req, res) => {
let users = await User.find();
try {
if (users.length < 1) {
return res.status(404).json({
error: "No users was found in DB"
});
}
return res.json(users);
} catch (err) {
return res.status(500).json({
error: "Something went wrong"
});
}
};
exports.updateUser = async (req, res) => {
try {
let user = await User.findByIdAndUpdate(
{ _id: req.profile._id },
{ $set: req.body },
{ new: true, useFindAndModify: false }
);
user.salt = undefined;
user.encrypted_password = undefined;
return res.json(user);
} catch (err) {
return res.status(400).json({
error: "You are not authorized to update this info"
});
}
};
You should send back 404 errors if you cant find any user in the database. 400 means bad request.
You can achieve what you are asking by wrapping the function with Promise. In your example, you should use the solution given by Ifaruki, because mongoose already supports promises.
function waitSeconds(seconds) {
return new Promise(res => {
setTimeout(() => {
res();
}, seconds * 1000)
})
}
async function foo() {
console.log("Hello");
await waitSeconds(5);
console.log("World");
}
Here you can learn more about async in javascript
I have a problem with .get request.
Somehow it is not returning anything? (GET http://localhost:8080/admin net::ERR_EMPTY_RESPONSE)
Any suggestions?
Get Route,With this I'm trying to filter all items by their username:
app.get("/:username", verify, (req, res) => {
console.log("Welcome to roffys server");
Todo.find({ username: req.params.username }).then((err, todo) => {
if (err) {
console.log("Error retrieving todos");
} else {
res.json(todo);
}
});
});
Verify function,here I'm verifying my auth-token,I console logged it and it is working fine:
const jwt = require("jsonwebtoken");
module.exports = function (req, res, next) {
const token = req.header("auth-token");
console.log("-----token", token);
if (!token) return res.status(401).send("Access Denied");
try {
const verified = jwt.verify(token, "secretkey");
req.user = verified;
} catch (err) {
res.status(400).send("Invalid token");
next();
}
};
FE Side with ReactJS :
componentDidMount() {
const { getAll, setPageCount } = this.props.actions;
axios
.get(`http://localhost:8080/${localStorage.getItem("username")}`, {
headers: {
"auth-token": localStorage.getItem("auth-token"),
},
})
.then((res) => {
getAll(res.data);
setPageCount();
console.log("--------res.data", res.data);
})
.catch((err) => {
console.log("err", err);
});
}
app.get("/:username", verify, (req, res, next) => {
console.log("Welcome to roffys server");
Todo.find({ username: req.params.username }).then((err, todo) => {
if (err) {
console.log("Error retrieving todos");
return next(err);
} else {
res.json(todo);
}
});
});
try to add next to your handler and call it when you receive an error.
I am building a login/Register portion of my app. Right now I'm using express-validator to check if an email exists in my collection.
This is my route:
var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body } = require('express-validator');
router
.route('/registration')
.get(function(req, res) {
console.log(0)
UserModel.find({}, (err, users) => {
console.log(1);
if (err) return res.status(500).send(err)
console.log(2);
return res.json(users);
})
})
.post(body('username_email').custom(value => {
console.log("value ", value);
console.log(3)
UserModel.findOne({ 'username_email': value }, (err) => {
console.log(4);
if (err) return res.status(409).send(err);
})
}), async(req, res, next) => {
console.log(5)
try {
let newUser = new UserModel(req.body);
let savedUser = await newUser.save();
console.log(6);
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router
In my component on the front end I have a function in my fetch which will catch the error if there is one.
handleErrors(response) {
if (!response.ok) {
console.log('This email exists!')
throw Error(response.statusText);
}
return response;
}
handleSubmit(event) {
event.preventDefault()
var { username, password } = this.state
var mailFormat = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
var error = false
if (!username.match(mailFormat)) {
this.setState({ usernameError: true })
error = true
} else {
this.setState({ usernameError: false })
}
if (password.length <= 8) {
this.setState({ passwordError: true })
error = true
} else {
this.setState({ passwordError: false })
}
console.log(`error ${error}`)
if (error == false) {
this.setState({ formError: false, formSuccess: true })
}
window.fetch('http://localhost:8016/users/registration', {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ username_email: username, password: password })
})
.then(this.handleErrors)
.then(function (response) {
console.log(`response ${response}`)
return response.json()
}).then(function (data) {
console.log('User created:', data)
}).catch(function (error) {
console.log(error);
});
}
The console.log in the fetch, handleErrors is being registered in the console, but why isn't the error status a 409 like I indicated.
Closer excerpt of post route!
.post(body('username_email').custom(value => {
console.log("value ", value);
console.log(3)
Is this the problem? Node style should have a error and callback?
UserModel.findOne({ 'username_email': value }, (err) => {
console.log(4);
if (err) return res.status(409).send(err);
})
}), async(req, res, next) => {
console.log(5)
try {
let newUser = new UserModel(req.body);
let savedUser = await newUser.save();
console.log(6);
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
UPDATE
I tried Nick's solution but I get this:
MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }
at Function.create (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/error.js:43:12)
at toError (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/utils.js:149:22)
at coll.s.topology.insert (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/operations/collection_ops.js:859:39)
at handler (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/topologies/replset.js:1155:22)
at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/connection/pool.js:397:18
at process._tickCallback (internal/process/next_tick.js:61:11)
POST /users/registration 500 312.485 ms - 51
^C
Two things I am noticing:
I get back MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }
which is the error from having a duplicate email, but number one where is E-mail already in use message in the console from the promise? And two how can I pass the error status "res.status(409).send(err);" from the promise?
The issue was that during your validation, you weren't returning the promise since the mongoose call is async. The rest the code ran before your validator was finished. I commented where you were missing the return.
router.route('/registration')
.get(function(req, res) {
UserModel.find({}, (err, users) => {
if (err) res.status(500).send(err)
res.json(users)
})
})
.post(body('username').custom(value => {
return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), async(req, res, next) => {
try {
let newUser = new UserModel(req.body)
let savedUser = await newUser.save(err => {
if (err) return res.json({ success: false, error: err })
return res.json({ success: true })
})
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router
UPDATE
Just read through express-validator docs. I think you would need to validate the errors during the request process
var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body, validationResult } = require('express-validator');
router.route('/registration')
.get(function(req, res) {
UserModel.find({}, (err, users) => {
if (err) res.status(500).send(err)
res.json(users)
})
})
.post(body('username').custom(value => {
return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), async(req, res, next) => {
// Checks for errors in validation
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
try {
let newUser = new UserModel(req.body)
let savedUser = await newUser.save(err => {
if (err) return res.json({ success: false, error: err })
return res.json({ success: true })
})
if (savedUser) return res.redirect('/users/registration?success=true');
return next(new Error('Failed to save user for unknown reasons'))
} catch (err) {
return next(err)
}
})
module.exports = router