exporting a module in a callback function? - node.js

is there a way to export some variables that are inside a callback function? for example, if i need to use room.room_id in another file, what should i do? i tried module.exports.roomId = room.room_id but roomId in another file appeared to be undefined.thanks!
var Room = require('../models/database').Room
exports.create = function (req, res) {
Room
.create({
room_name: req.body.roomName
})
.complete(function () {
Room
.find({where: {room_name: req.body.roomName}})
.success(function (room) {
// if(err) console.log(err);
res.redirect('rooms/videochat/' + req.body.roomName + '/' + room.room_id);
console.log("room_id: " + room.room_id);
module.exports.roomId = room.room_id;
})
})
};

You can't do it like that because modules are evaluated synchronously and you're mutating module.exports some time in the future. What you need to do is supply a callback and either pass the value in or use the callback as an indicator that you can successfully read from the exported property.

This is not the best way to solve this problem, because modules are read once synchronously and cached but your code seems to handle requests and responses.
You will want rather export something like this:
var rooms = {};
exports.create = function (req, res, next) {
Room.create({
room_name: req.body.roomName
}).complete(function () {
Room.find({where: {room_name: req.body.roomName}})
.success(function (room) {
res.redirect('rooms/videochat/' + req.body.roomName + '/' + room.room_id);
rooms[req.body.roomName] = room.room_id;
});
});
};
exports.rooms = rooms;
If you are using Express.js, you can register in another place a route like this:
var roomsManager = require('./path/to/the/module');
//handle the create room endpoint
app.post('/room', roomsManager.create);
//get the room_id given a room name:
console.log('the room id of "some room" is:', roomsManager.rooms["some room"]);

Related

Retrieve from firebase and render in ejs

So I'm basically trying to do a "foreach" loop for my ejs frontend but I can't seem to res.render the snapshot in the server.js. I can't seem to get the variable into the res.render(). I have no problem retrieving the data from firebase FYI.
I've already tried various methods such as moving it in the ref.on etc
app.get('/', function (req, res) {
var ref = database.ref('Courses');
// var ref = firebase.database().ref("users");
ref.on("value", function (snapshot) {
snapshot.forEach(function (childSnapshot) {
var childData = childSnapshot.val();
});
});
res.render('pages/index', {
ChildData: childData
});
});
Data is loaded from Firebase asynchronously, since it may take some time. Instead of waiting for the data to come back from the server, the main code of your app continues straight away. Then when the data is available, your callback is called with that data.
This means that any code that needs the data from the database needs to be inside that callback.
app.get('/', function(req, res) {
var ref = database.ref('Courses');
ref.once("value", function (snapshot) {
var childData;
snapshot.forEach(function (childSnapshot) {
childData = childSnapshot.val();
});
res.render('pages/index', {
ChildData:childData
});
});
});
The main changes:
I moved the res.render into the callback, so that it runs after the data is available.
I declare the var childData before the loop, so that it's also available to the code outside of the loop.
I use once instead of on, so that the data is only loaded once.
Use this
app.get('/', function(req, res) {
var ref = database.ref('Courses');
// var ref = firebase.database().ref("users");
ref.on("value", function (snapshot) {
snapshot.map(function (childSnapshot) {
var childData = childSnapshot.val();
});
res.render('pages/index', {
ChildData:childData
});
});
});
Move the response send method inside the callback of ref.on and change forEach to map becuase map is synchronous so it will complete the iterations first and will send the data in response

How To Bind Node-js DB Query to Web Form

I'm using node and postgres, I'm new to writing async function, what I'm trying to do is a very simple query that will do a total count of records in the database, add one to it and return the result. The result will be visible before the DOM is generated. I don't know how to do this, since async function doesn't return value to callers (also probably I still have the synchronous mindset). Here's the function:
function generateRTA(callback){
var current_year = new Date().getFullYear();
const qry = `SELECT COUNT(date_part('year', updated_on))
FROM recruitment_process
WHERE date_part('year', updated_on) = $1;`
const value = [current_year]
pool.query(qry, value, (err, res) => {
if (err) {
console.log(err.stack)
} else {
var count = parseInt(res.rows[0].count) + 1
var rta_no = String(current_year) + '-' + count
callback(null, rta_no)
}
})
}
For the front-end I'm using pug with simple HTML form.
const rta_no = generateRTA(function (err, res){
if(err){
console.log(err)
}
else{
console.log(res)
}
})
app.get('/new_application', function(req, res){
res.render('new_application', {rta_number: rta_no})
});
I can see the rta_no in console.log but how do I pass it back to the DOM when the value is ready?
Based on the ajax call async response, it will update the div id "div1" when it gets the response from the Node js .
app.js
app.get("/webform", (req, res) => {
res.render("webform", {
title: "Render Web Form"
});
});
app.get("/new_application", (req, res) => {
// Connect to database.
var connection = getMySQLConnection();
connection.connect();
// Do the query to get data.
connection.query('SELECT count(1) as cnt FROM test ', function(err, rows, fields) {
var person;
if (err) {
res.status(500).json({"status_code": 500,"status_message": "internal server error"});
} else {
// Check if the result is found or not
if(rows.length==1) {
res.status(200).json({"count": rows[0].cnt});
} else {
// render not found page
res.status(404).json({"status_code":404, "status_message": "Not found"});
}
}
});
// Close connection
connection.end();
});
webform.pug - Via asynchronous call
html
head
script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')
script.
$(document).ready(function(){
$.ajax({url: "/new_application", success: function(result){
$("#div1").html(result.count);
}});
});
body
div
Total count goes here :
#div1
value loading ...
That seems okay, I'm just not sure of this:
The result will be visible before the DOM is generated
This constraint defeats the purpose of async, as your DOM should wait for the returned value to be there. Instead of waiting for it you could just render the page and once the function returns and runs your callback update the value.
Also, perhaps it's worth having a look into promises

Render after db connection is completed - Nodejs LevelDB

I'm new to Nodejs, Express and Leveldb.
I created db using level and want to pass parameter.
exports.index = function(req, res) {
var models_array = [];
db.models.createValueStream()
.on('data', function (data) {
console.log(data.name);
models_array.push(data.name);
console.log(models_array); // 1st
});
console.log(models_array); //2nd
res.render('home', {
title: 'Home',
models:models_array
});
};
This is my code but 2nd console.log(models_array) is returning null because they are running asynchronously.
Even 1st console.log is returning what I expected.
How can make this work properly?
So that I can pass proper data to template.
I found myself. I can use .once()
var models_array = [];
db.models.createValueStream()
.on('data', function (data) {
models_array.push(data.name);
}).once('end', function() {
res.render('home', {
title: 'Home',
models:models_array
});
});

I'd like to follow the DRY principle and simplifying routing on this express server?

I have the following express server set up (server is just express() from another file). I am sure there is a way to simplify this to only one server.get() but I haven't been able to figure out how. Any help or points in the right direction would be appreciated.
module.exports.api = function (server, fs) {
server.get('/api/getData/:uuid', function (req, res) {
fs.readFile(__dirname + '/data.json', function (err, data) {
if (err) throw err;
data = JSON.parse(data);
data.forEach(function (match) {
match['uuid'] = match['x'] + '-' + match['y'];
});
var match = data.filter(function (e) {
return e.uuid == req.params.uuid
})[0];
res.send(200, match);
});
});
server.get('/api/getData', function (req, res) {
fs.readFile(__dirname + '/data.json', function (err, data) {
if (err) throw err;
data = JSON.parse(data);
data.forEach(function (match) {
match['uuid'] = match['x'] + '-' + match['y'];
});
res.send(200, data);
});
});
};
Here's a solution that just moves the common code into a shared function, yet still uses the two routes for routing clarity:
function getData(res, uuid) {
fs.readFile(path.join(__dirname, 'data.json'), function (err, fileData) {
if (err) {
return res.send(500);
}
let data = JSON.parse(fileData);
data.forEach(function(match) {
match['uuid'] = match['x'] + '-' + match['y'];
});
if (uuid) {
var match = data.filter(function (e) {
return e.uuid == uuid;
})[0];
}
res.send(200, match);
});
}
module.exports.api = function (server, fs) {
server.get('/api/getData/:uuid', function (req, res) {
getData(res, req.params.uuid);
});
server.get('/api/getData', function (req, res) {
getData(res);
});
};
This changes the following things:
Puts shared code into getData() function that is called from both routes.
Sends an error response if fs.readFile() has an error
Creates new local variable so it doesn't assign back to a function argument which is now a less desirable practice because it prevents some interpreter optimizations.
Uses path.join() to join parts of a path in a more cross platform way.
FYI, unless the data in data.json actually changes from time to time, you could just read this data into a variable once and then cache it rather than rereading it on every one of these requests.
Note: You could use routing wildcards and reduce your code to a single route, but this is mostly considered an anti-pattern because wildcards often match much more than you want, creating situations where you have manually trigger 404 errors for things you didn't intend to match that ended up matching your routing wildcard. So, it is considered a good thing to explicitly declare the routes you intend to match rather and just share the appropriate implementation code rather than trying to collapse things down to a single route that matches more than one form of URL.
There are, of course, always exceptions by remember that the goal is clear, correct, maintainable, reliable code, not necessarily the fewest number of routes.
If you just want to cache the data.json data at server start up time, you can use require() to load and parse it for you like this and then there's really no reason for the sharef fucntion:
const cacheData = require('./data.json');
cacheData.forEach(function(match) {
match['uuid'] = match['x'] + '-' + match['y'];
});
module.exports.api = function (server, fs) {
server.get('/api/getData/:uuid', function (req, res) {
let match = cacheData.filter(function (e) {
return e.uuid == req.params.uid;
})[0];
res.send(match);
});
server.get('/api/getData', function (req, res) {
res.send(cacheData);
});
};

How to synchronize sqlite3 update with command execution

I am working with a NodeJS project where i need to update a table and afterwards restart a service. Unfortunately the service restarts before the table has been updated. So i assume this is a normal async behaviour.
How do i synchronize this?
var sqlite3 = require('sqlite3').verbose();
var express = require('express');
var app = express();
var router = express.Router();
var db = new sqlite3.Database('/home/test/testApp.db', 'OPEN_READWRITE');
router.route('/')
.get(function(req, res) {
res.render('index', { data: dbConfigRow });
})
.post(function(req, res) {
// console.log(req.body);
db.serialize(function() {
for (var key in req.body) {
db.run("UPDATE config SET " + key + "='" + req.body[key] + "'");
}
exec('systemctl restart demoApp');
});
res.json(200);
});
You should check out Async or any one of the popular promise libraries (When.js, Q.js, Bluebird).
Any of these should solve your problem. In Async it might look something like this using series:
.post(function(req, res) {
async.series([
function(callback){
db.serialize(function() {
for (var key in req.body) {
db.run("UPDATE config SET " + key + "='" + req.body[key] + "'");
}
callback()
})
},
function(callback){
exec('systemctl restart demoApp'); //Assuming this is synchronous
callback()
}
],
function(error, results){ //Using the optional callback
res.send(200);
}
);
});
This assumes db.run is synchronous (it looks like it is).
All this said, it looks like your current implementation was returning 200 before it finished all the db/restarting tasks. You might try moving the response after the exec. That may also work. Let me know if this solves your issue.

Resources