Organize MongoDB request on Node.JS - javascript

I'm actually creating a chat like Discord with servers and channels using Node.JS, MongoDB and Mongoose.
Actually, my structure is this one:
https://github.com/copostic/My-Simple-Chat/tree/master/models
But to get the conversations, I have to make so much nested functions and I would like to know if there was a better way to organize my code..
Here's the code with the nested functions, I'm trying to get the message list of each channel of each server:
"use strict"
const Server = require('../models/server'),
Channel = require('../models/channel'),
Message = require('../models/message'),
User = require('../models/user');
exports.getChannels = function (req, res, next) {
// Only return one message from each conversation to display as snippet
Server.find({members: req.session._id})
.select('_id')
.exec(function (err, servers) {
if (err) {
res.send({ error: err });
return next(err);
}
servers.forEach(function (server) {
Channel.find({ serverId: server })
.exec(function (err, channels) {
// Set up empty array to hold conversations + most recent message
let fullConversations = [];
channels.forEach(function (channel) {
Message.find({
'channelId': channel._id
})
.sort('creationDate')
.limit(1)
.populate({
path: "author",
select: "profile.firstName profile.lastName"
});
.exec(function (err, message) {
if (err) {
res.send({
error: err
});
return next(err);
}
fullConversations.push(message);
if (fullConversations.length === conversations.length) {
return res.status(200).json({
conversations: fullConversations
});
}
});
});
});
});
});
};
Thanks a lot

Related

Node.js - PostgreSQL (pg) : Client has already been connected. You cannot reuse a client

I am just trying to write simple register/login system.
I am trying to find if username exists. Here is the steps :
Go localhost:3000/users/register page
Fill in all fields and click register button
Checking my command line if username exists it should print it with console.log
Everything works fine until now.
When I go back to the register page, I fill in all fields again and click register button. Then it throws it in command line :
Error: Client has already been connected. You cannot reuse a client.
at Client._connect (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:91:17)
at C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:310:10
at new Promise (<anonymous>)
at Client.connect (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:309:10)
at Object.module.exports.findUserById (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\database\register_sql.js:8:22)
at C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\routes\users.js:37:29
at Layer.handle [as handle_request] (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:137:13)
at next (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:131:14)
at Route.dispatch (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:112:3)
I dont understand because I already end my client after I call my method.
register_sql.js :
module.exports.findUserById =(async (username) =>{
try {
await client.connect();
console.log('Connected successfuly');
const result = await client.query("select * from users where username = ($1)", [username]);
console.log(result.rows[0]['username']);
await client.end();
console.log('Client disconnected...');
} catch (err) {
console.log(err);
}
});
I call register_sql.js in users.js. Here is users.js :
const router = express.Router();
const registerSQL = require('../database/register_sql');
router.route('/register')
.get((req, res, next) => {
res.render('register');
})
.post((req, res, next) => {
const {
username,
password,
password2
} = req.body;
let errors = [];
if (!username || !password || !password2) {
errors.push("Please fill in all fields!");
}
if (password != password2) {
errors.push("Passwords do not match!");
}
if (password.length < 6) {
errors.push("Password has to be at least 6 characters!");
}
if (errors.length > 0) {
res.render('register', {
errors
});
} else {
registerSQL.findUserById(username);
res.redirect('/');
}
});
module.exports = router;
Thank you for helping!
With node-postgres (which is the one you're using) I've only made it work using the pool do manage the connections.
const pg = require('pg')
const pool = new pg.Pool();
pool.connect(function(err, client, done) {
if(err) {
return console.error('connexion error', err);
}
client.query("select * from users where username = ($1)", [username], function(err, result) {
// call `done()` to release the client back to the pool
done();
if(err) {
return console.error('error running query', err);
}
console.log(result.rows[0]['username'])
});
});
I had the same problem, dont create the new Client outside the function.
- const client = new pg.Client(connection);
-
function test() {
+ const client = new pg.Client(connection);
+
client.connect(err => {
if (err) {
console.log(err);
return;
}
client.query('select 123', [], (err, data) => {
if (err) {
console.log(err);
} else {
console.log('DATA:', data.rows[0]);
}
client.end();
});
});
}
I managed to fix this problem without using pool. Maybe that's not the most correct solution, but it works.
First create a separate js file, where you connect the client and export it:
const pg = require('pg')
const client = new pg.Client('your connection string')
client.connect()
module.exports = client
Then you just use the exported client, which has already been connected, so it won't reconnect again on each request. Be sure to import the client from the js file where you connect it.
const client = require('../db')
const register = async (req, res) => {
const {email, password, username} = req.body
const hashedPassword = await bcrypt.hash(password, 10)
const command = `insert into users(email, username, password, created) VALUES ('${email}', '${username}', '${hashedPassword}', current_timestamp)`
await client.query(command, (err, result) => {
err ? res.json({error: err.detail}) : res.json({message: 'User created!'})
})
}
well the problem occur because you haven't closed the connection to database.
Remember you have to close the connection before you sent something to client like that:
try {
await client.connect();
const result = await client.query(query, values);
await client.end();
res.status(201).send({
result,
});
} catch (err) {
return res.send({
error: err.detail,
message: "Can't create a new user, please check your info again!",
});
}
Pool approach is better practice but if someone want to connect with Client approach then this solution will work.
Code which will work with Client approach :
client.connect();
client.query(`select * from users where username = ($1)`, (err, result)=>{
try{
console.log("Test", result)
res.send(result)
} catch{
console.log("err", err)
res.status(500).json(err);
}
client.end();
})

Node.js Express execute inside app.post()

I have a problem right now that I can't solve by myself. I assume you know more here. I'm just getting started.
By using the following code I'm getting a list of customers:
app.get("/customers", customers.findAll);
I wanted to add authentication. But now I don't know how to execute "customers.findAll" and get the value as JSON.
app.get("/customers", verifyToken, (req, res) => {
jwt.verify(req.token, 'justAtest, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
// execute customers.findAll
}
});
});
Customers is integrated via a requirement
const customers = require("../controllers/customer.controller.js");
The contents are as follows:
exports.findAll = (req, res) => {
Customer.getAll((err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while retrieving customers."
});
else res.send(data);
});
};
Do you have any ideas?
Thank you in advance.
Grettings
Rok
You achieve that using something called "middleware". Explore it since it is very important.
Basically :
app.get("/customers", verifyToken,customers.findAll);
Wherre verify token is a funtion that has 3 parameters: req, res and 3rd one called "next".
So your verify token function would look something like:
(req, res,next) => {
jwt.verify(req.token, 'justAtest, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
next();
}
});
}
I took it from your snippet. Basically if you want to jump to customeeers.finalAll, just call "next" and it jumps to next function :D.

Node.js MySQL routes + model structure

Hello I am new to building node js and express web applications and am trying to understand how to structure route handling + model code using MySQL and node.js. Here is an example of my current login.
router.post('/login', function (req, res, next) {
var email = req.body.email;
var password = req.body.password;
var sqlQuery = 'SELECT * FROM users WHERE email = ?';
checkUsername();
function checkUsername() {
connection.query(sqlQuery,[email], function (error, results, fields) {
if (error) return next(error);
if(results.length == 0) {
res.status(401).json({ message: 'invalid credentials' });
} else {
checkPassword(results[0].password, results[0]);
}
});
}
function checkPassword(hash, result) {
bcrypt.compare(password, hash, function(err, match) {
if (match) {
var userToken = jwt.sign({id: result.id}, config.JWTSecret, { expiresIn: '1h' });
delete result.password;
res.status(200).json({token: userToken, user_data: result});
} else {
res.status(401).json({ message: 'invalid credentials' });
}
});
}
});
as you can see currently all of the logic is being performed in the route handler. What is the advantage of extracting this code to a model object? Should the response be sent in the model or route handler? How would you structure your code for this particular example using a user object and more generally when using MVC with express and MySQL?
Thanks!

Send 2 array to view in sailjs

I am new to sails.js and when I'm trying to learn it by making a simple web app, I encountered a problem. My app is about class management. My model has 3 table: student, course and list. The student table save information about students, course save information about courses. The list.table save information about which student is in a course and their mark, their absent days in this class. I used res.view() and it worked for any view that need one array. However, when I try to write function which need information from 2 model: student and course, res.view() didnt help (or I dont know to do it properly)
module.exports = {
'new': function(req, res, next) {
Course.find(function foundCourses (err, courses) {
if (err) return next(err);
res.view({courses: courses});
});
/* Student.find(function foundStudents (err, students) {
if (err) return next(err);
res.view({
students: students
});
}); */
},
//more other code here
};
There, I want to send both array to view but I dont know how. Sails.js only let me send one array if I do like that. Please help me . Thank you a lot!
You can't call res.view twice. You need to gather all data first and than send it to view.
2 methods:
module.exports = {
'new': function(req, res, next) {
var foundCourses = function (err, courses) {
if (err) return next(err);
Student.find(function foundStudents (err, students) {
if (err) return next(err);
res.view({
courses: courses,
students: students
});
});
});
Course.find(foundCourses);
},
//more other code here
};
or you can use async.parallel
module.exports = {
'new': function(req, res, next) {
var dataCallback = function (err, data) {
if (err) return next(err);
var courses = data[0];
var students = data[1];
res.view({
courses: courses,
students: students
});
};
async.parallel([
function(callback) {
Course.find(callback);
},
function(callback) {
Student.find(callback);
}
],dataCallback);
},
};

How to change DataSource programmatically in loopback

Suppose each users has huge amount of data that no need to store those in single table. I want to store each user's data in separate database for faster query.
I want when any user logging in loopback, change datasets based on users connection strings that stored in User model.
I read all of loopback docs and try so many practice to do this but i can't implement this.
I try this in server/server.js:
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
app.datasources.db= ds;// <----- IT DOES'NT WORKING
next();
});
});
but IT DOES'NT WORKING (marked in code).
Any idea to solve this issue?
You can use attachTo() function to attach a datasource based on conditions.
app.use(loopback.context());
app.use(loopback.token());
app.use(function (req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.User.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
//console.log('server.js');
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
// This way you can attach memory datasource to model.
// This only covers models defined using app.model();
var models = app.models();
models.foreach(function(model) {
model.attachTo(app.dataSources.memory);
});
next();
});
});
Also use defined datasource, memory in datasource.json.
{
"memory": {
"name": "memory",
"connector": "memory"
}
}
Please refer this too: app.models()

Categories