Why does the 1st middleware of the array needs to call next() but the 2nd does not? - express

I am new in web development. While following the Node Express Mozilla Tutorial, I came accross this array of controller middlewares that made me confused regarding the use of next().
This array has 3 middlewares:
One for making sure a certain form parameter is in array format
One for doing validation and sintizaiton of the for parameters
One for processing and giving a response to the request
My doubt is: why does the 1st one calls next() but the 2nd does not?
Below the block of code with the array of controller middlewares:
// Handle book create on POST.
exports.book_create_post = [
// Convert the genre to an array.
(req, res, next) => {
if(!(req.body.genre instanceof Array)){
if(typeof req.body.genre ==='undefined')
req.body.genre = [];
else
req.body.genre = new Array(req.body.genre);
}
next();
},
// Validate and sanitise fields.
body('title', 'Title must not be empty.').trim().isLength({ min: 1 }).escape(),
body('author', 'Author must not be empty.').trim().isLength({ min: 1 }).escape(),
body('summary', 'Summary must not be empty.').trim().isLength({ min: 1 }).escape(),
body('isbn', 'ISBN must not be empty').trim().isLength({ min: 1 }).escape(),
body('genre.*').escape(),
// Process request after validation and sanitization.
(req, res, next) => {
// Extract the validation errors from a request.
const errors = validationResult(req);
// Create a Book object with escaped and trimmed data.
var book = new Book(
{ title: req.body.title,
author: req.body.author,
summary: req.body.summary,
isbn: req.body.isbn,
genre: req.body.genre
});
if (!errors.isEmpty()) {
// There are errors. Render form again with sanitized values/error messages.
// Get all authors and genres for form.
async.parallel({
authors: function(callback) {
Author.find(callback);
},
genres: function(callback) {
Genre.find(callback);
},
}, function(err, results) {
if (err) { return next(err); }
// Mark our selected genres as checked.
for (let i = 0; i < results.genres.length; i++) {
if (book.genre.indexOf(results.genres[i]._id) > -1) {
results.genres[i].checked='true';
}
}
res.render('book_form', { title: 'Create Book',authors:results.authors, genres:results.genres, book: book, errors: errors.array() });
});
return;
}
else {
// Data from form is valid. Save book.
book.save(function (err) {
if (err) { return next(err); }
//successful - redirect to new book record.
res.redirect(book.url);
});
}
}
];

Related

Query works but cant retrieve the data

I am new to Node.js (3 days total experience). I am using Node.js and the tedious package to query a database (azure SQL). I use the example as explained here: https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-nodejs?tabs=macos
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on("connect", err => {
if (err) {
console.error(err.message);
} else {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
"SELECT * FROM clients",
(err, rowCount, columns) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
}
);
request.on("row", columns => {
columns.forEach(column => {
console.log("%s\t%s", column.metadata.colName, column.value);
});
});
connection.execSql(request);
}
});
I have two issues:
I do not know how to get the queried data into an object and
If I run the script it does print the items to the console, but it doesn't close the connection after it has done so. If I add a connection.close() at the bottom, it will close the connection before its done. I get the feeling that node.js executes everything at the same time (I am used to Python..).
Update
I found a way to close the connection, to my understanding the request object has several "events" that are predefined by the library. It seems I need to add the event "done" through request.on('done', ...) in order to make sure that it can even BE done. My updated code looks like this:
var connection = new Connection(config);
connection.connect(function(err) {
// If no error, then good to go...
executeStatement();
}
);
connection.on('debug', function(text) {
//remove commenting below to get full debugging.
//console.log(text);
}
);
function executeStatement() {
request = new Request("SELECT * FROM clients", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(rows) {
_.forEach(rows, function(value, collection){
console.log(value)
console.log(value.value);
console.log(value.metadata.colName)
console.log(collection)
})
});
request.on('done', function(rowCount, more) {
console.log(rowCount + ' rows returned');
});
// In SQL Server 2000 you may need: connection.execSqlBatch(request);
connection.execSql(request);
}
Anyways, your help would be much appreciated!
Regards
Pieter
The package tedious is synchronous package, it uses the callback to return results. So when we call connection.close(), it will disable connection and stop the callback function. If will want to close the connection, I suggest you use async package to implement it.
For example
const { Connection, Request } = require("tedious");
const async = require("async");
const config = {
authentication: {
options: {
userName: "username", // update me
password: "password", // update me
},
type: "default",
},
server: "your_server.database.windows.net", // update me
options: {
database: "your_database", //update me
encrypt: true,
validateBulkLoadParameters: true,
},
};
const connection = new Connection(config);
let results=[]
function queryDatabase(callback) {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request("SELECT * FROM Person", (err, rowCount) => {
if (err) {
callback(err);
} else {
console.log(`${rowCount} row(s) returned`);
callback(null);
}
});
request.on("row", (columns) => {
let result={}
columns.forEach((column) => {
result[column.metadata.colName]=column.value
console.log("%s\t%s", column.metadata.colName, column.value);
});
// save result into an array
results.push(result)
});
connection.execSql(request);
}
function Complete(err, result) {
if (err) {
callback(err);
} else {
connection.close();
console.log("close connection");
}
}
connection.on("connect", function (err) {
if (err) {
console.log(err);
} else {
console.log("Connected");
// Execute all functions in the array serially
async.waterfall([queryDatabase], Complete);
}
});
connection.connect();
Besides, you also can use the package mssql. It supports asynchronous methods and depends on package tedious. We can directly call close after querying.
For example
const mssql = require("mssql");
const config = {
user: "username",
password: "password",
server: "your_server.database.windows.net",
database: "your_database",
options: {
encrypt: true,
enableArithAbort: true,
},
};
let pool = new mssql.ConnectionPool(config);
async function query() {
try {
await pool.connect();
const request = pool.request();
const result = await request.query("SELECT * FROM Person");
console.dir(result.recordset);
await pool.close();
console.log(pool.connected);
} catch (error) {
throw error;
}
}
query().catch((err) => {
throw err;
});
You can custom a class first and declare an Array to save ojects such as:
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
Here my sql statement returns 2 properties, so every time the loop takes out two elements from the ColumnValue[].
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
The code is as follows:
const { Connection, Request } = require("tedious");
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
}
// Create connection to database
const config = {
authentication: {
options: {
userName: "<***>", // update me
password: "<***>" // update me
},
type: "default"
},
server: "<****>.database.windows.net", // update me
options: {
database: "<***>", //update me
encrypt: true
}
};
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on ("connect", err => {
if (err) {
console.error(err.message);
} else {
queryDatabase();
}
});
function queryDatabase() {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
`SELECT TOP 2 pc.Name as CategoryName,
p.name as ProductName
FROM [SalesLT].[ProductCategory] pc
JOIN [SalesLT].[Product] p ON pc.productcategoryid = p.productcategoryid`,
(err, rowCount) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
connection.close();
}
);
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
connection.execSql(request);
}
this article should help you, to solve all the issues you are facing...which were the same I had when I started using Node :)
https://devblogs.microsoft.com/azure-sql/promises-node-tedious-azure-sql-oh-my/

mongoose save by reference multiple document

I'd like to make new document by reference of two documents.
**app.post('/student_badge/register', async (req, res) => {
const name = req.body.name;
const category = req.body.category;
People.find({name: name}, '_id', function (err, doc) {
if (err) return handleError(err);
var obj = eval(doc);
id = obj[0]._id;
})
Badge.find({category: category}, 'points title', function (err, doc) {
if (err) return handleError(err);
var obj2 = eval(doc);
points = obj2[0].points;
title = obj2[0].title;
console.log(title + " " + points);
});
data = {
id: id,
title: title,
points: points
}
console.log("data: " + data);
const sbadge = new StudentBadge(data);
sbadge.
save()
.then(result => {
res.status(201).json({
message: 'Post created successfully!',
post: result
});
})
.catch(err => {
console.log(err);
});
});**
But I cannot call three variables like id, title, points to store them in 'data'.
How can I call variables?
Thanks
Your code does not work because the variables you are trying to access, i.e. id, title, points, are being set on a callback function that gets executed asynchronously.
I would suggest using async/await instead of callbacks so that you can thereafter use the data from the other documents you are querying in the same function. In addition, I suggest to use findOne() since you only access the first entry in db.
Something like the example below should work: (I have abstracted the middleware in a separate function for clarity to use with express)
const createStudentBadge = async (req, res, next) => {
const {name, category} = req.body;
let person, badge;
try {
person = await Person.findOne({name}); // shortcut for {name: name}
badge = await Badge.findOne({category});
} catch(err) {
// handle error
}
if (!person || !badge) {
// Handle case where no document has been found in db
// This case will not throw an error when calling find()
}
data = {
id: person._id,
title: badge.title,
points: badge.points
}
const studentBadge = new StudentBadge(data);
try {
await studentBadge.save();
} catch(err) {
// handle error
}
res.status(201).json({
message: 'Post created successfully!',
post: studentBadge
});
}
app.post('/student_badge/register', createStudentBadge);
If you wanted to perform the querying in parallel, you could make use of Promise.all() and run both queries at the same time. More info can be found at MDN documentation

validating with expressjs inside (res, req) function instead of inside a middleware

I'm using express-validator library to validate on the backend. This is the library:
https://express-validator.github.io/docs/index.html
I have this code
// ...rest of the initial code omitted for simplicity.
const { check, validationResult } = require('express-validator');
app.post('/user', [
check('username').isEmail(),
check('password').isLength({ min: 5 })
], (req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});
Is it possible to call validation function inside the (req, res) =>{ }
I have some requirements where I need to check what has arrived in via request before I can construct the validation array.
Basically I would like to be able to do this:
app.post('/user', (req, res) => {
const { importantParam, email, password, firstname, lastname } = request.body
let validateThis = []
if(importantParam == true)
validateThis = [
check('username').isEmail(),
check('password').isLength({ min: 5 })
]
else
validateThis = [
check('username').isEmail(),
check('password').isLength({ min: 5 })
check('firstname').isLength({ min: 5 })
check('lastname').isLength({ min: 5 })
]
runValidationFunction(validateThis)
//now below code can check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});
This is what I need to do, construct the validation array based on if one of the params has a specific value. I can't figure out how to do that with the first example since it seems request is not possible to access when taking this approach
app.post('/user', validationArray, (req, res) => {}
Any ideas how I can call express-validate validate function directly inside
(req, res) => {}
What you can do is to run a check inside a custom validation. You should require validator as well.
const { check, body, param } = require('express-validator/check');
const { validator } = require('express-validator');
const checkValidation = () => {
return [
check('importantParam')
.custom((value, {req}) => {
const data = req.body;
if (!validator.isEmail(data.username)) throw new Error("Not valid Email");
if (!validator.isLength(data.password, {min:5})) throw new Error("Not valid password");
// if importantParam is false, add the following properties to validation
if (!value) {
if (!validator.isLength(data.firstname, {min:5})) throw new Error("Not valid firstname");
if (!validator.isLength(data.lastname, {min:5})) throw new Error("Not valid lastname");
}
return true;
})
];
};
app.post('/user', checkValidation(), (req, res) => {
//now below code can check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
// do stuff here
});
Hope it helps!

expressjs doesn't wait till query is done

I am using expressjs to retrieve data from elasticsearch and send back to my angular app at the front end. Currently I am facing a problem since expressjs doesn't wait until the query execution is finished. I searched for a solution for that and the community says use "Promise or Sync". But I cant figure out where should I use it. I tried to use it but I am getting errors.
This is where I am receiving the request from the frontend and calling the elasticsearch query for send the response.
api.post('/clsDependencies', (req, res) => {
classDependencies(req.body.className);
res.json(messages);
});
This the function for querying the elasticsearch.
function classDependencies(csName) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
search('testclass', body)
.then(results => {
results.hits.hits.forEach((hit, index) => hit._source.dependencies.forEach(
function(myClass){
messages.push({text: myClass.methodSignature , owner: `\t${++nmb} -
${myClass.dependedntClass}`});
}))})
.catch(console.error);
};
Expected data gets initialized to the variable(messages) which I am trying to send back to the front end. But the variable doesn't get initialized at the time when response is send back. What Should I do to wait till the query execution finish before send back the data to frontend.
EDIT
messages is defined outside of both functions.
function classDirectory(className) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: className
}
}
}
};
return search('testclass', body).then(results => {
results.hits.hits.forEach((hit, index) =>
getDirectories(hit._source.JarFileName));
return messages;
})
.catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
function getDirectories(jarName) {
let body = {
size: 20,
from: 0,
query: {
match: {
jarFileName: {
query: jarName
}
}
}
};
return search('testjar', body).then(results => {
results.hits.hits.forEach((hit, index) =>
messages.push({text: hit._source.jarFileName , owner: `\t${++nmb} -
${hit._source.directory}`})
);
return messages;
})
.catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
The Javascript interpreter does not "block" when you make asynchronous calls. This has absolutely nothing to do with Express.
Your call to search() is non-blocking so while it's in process, classDependencies() returns and the rest of your code continues to run. This is the way asynchronous calls in Javascript work.
If you want to call res.json() when classDependencies() is done, then return a promise from it and call res.json() when that promise resolves.
You could do something like this:
api.post('/clsDependencies', (req, res) => {
classDependencies(req.body.className).then(messages => {
res.json(messages);
}).catch(err => {
res.status(500).send(something here);
});
});
function classDependencies(csName) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
return search('testclass', body).then(results => {
let messages = [];
results.hits.hits.forEach((hit, index) => hit._source.dependencies.forEach(function(myClass) {
messages.push({
text: myClass.methodSignature,
owner: `\t${++nmb} - ${myClass.dependedntClass}`
});
}));
// make messages be the resolved value of the returns promise
return messages;
}).catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
api.post('/clsDirectory', (req, res) => {
classDependency(req.body.className, res);
});
function classDependency(csName, cb) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
search('testclass', body)
.then(results => {
results.hits.hits.forEach((hit, index) =>
hit._source.dependencies.forEach(
function(myClass){
messages.push({text: myClass.methodSignature , owner: `\t${++nmb} -
${myClass.dependedntClass}`});
}));
cb.json(messages);
})
.catch(console.error);
};

Express REST API - Delete Method

I am getting stuck on the delete method for my API. My application requires a user to log in and then he can add courses. The courses are stored in a nested array inside the User model. I want the user to be able to cancel (delete) a course from the view and then have the course deleted from the user's profile on the server. I am getting a 404 response event though the variables I am comparing are identical.
This is my ajax call to delete a specific course:
jQuery.ajax({
url: "/test/signups/5387c1a0fb06e48f4658170c",
type: "DELETE",
success: function (data, textStatus, jqXHR) {
console.log("Post resposne:");
console.dir(data);
console.log(textStatus);
console.dir(jqXHR);
}
});
This is my delete method:
app.delete('/test/signups/:id', isLoggedIn, function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
var found = false;
var singlesignup = user.signup.filter(function(e){ return e._id == req.params.id })[0]
user.signup.forEach(function (singlesignup, index) {
if (singlesignup._id === req.params.id) {
found = index;
}
});
if(found) {
user.signup.splice(found, 1);
res.json(200, {status: 'deleted'});
} else {
res.json(404, {status: 'invalid survey question deletion'});
}
}
});
});
The _id values in mongodb are not strings, they are instances of the ObjectId class and they don't work correctly with the == or === operators. It's completely a nuisance. But anyway try converting them to strings before comparing: singlesignup._id.toString() === req.params.id. To get it truly correct in the long run, make sure you handle all the cases of null, string or ObjectId. Consider a helper library such as objectid.