Dynamically render data in node.js - node.js

This is a typical route in node.js that has a pseudo-code to connect to a database, get some data using a query and then pass them to a page to be rendered,
router.get('/', function(req, res){
db-connect(function(err, db) {
if (err) {
return console.log('error');
}
db.query('select * from table', function(err, results){
if (err) {
return console.log('error');
}
res.render('index',{
'title':'my title',
'pageHeader': 'my header',
'results': results //dynamic ???
});
});
}); //connect
});//router get
I am using this pseudo-code to ask a general question :
The results data are dynamic, maybe the query will take a while, so the results do not get to the page fast, so I guess the rendering will also take a while.
How can I render static data immediatly (title and pageHeader) and the dynamic part (results) as soon as it is ready ?
Do I have to use another function or another syntax?
Thank you

res.render populates your template and sends it to the client (browser). You cannot send "a bit more" when it's ready at a later stage.
Either make the client wait for the data, or send your title and header first, and use XHR (javascript) on the browser to get the rest.

Related

How can I run a search in my postgress database, return the result and then send it to be displayed on an ejs page?

I am using Node.js w/ Heroku & Postgres
My code needs to pull data from Postgres, store that data, send that data to a webpage to be displayed.
The code looks like this. I want to get the information from
SELECT * FROM people;, then I want to store it somehow, and then I want to res.render it onto 'pages/sql'.
So when I visit myURL.com/sql I will see a stringified version of the results gotten from the database. So far I can see this information in my console when I run heroku logs --tail but I want it to be rendered on a live webpage visible to anybody.
Ideally I'd like to be able to do a lot more than this, but this would help for now.
By wanting to do much more I'd like to eventually find out how to have a full ejs webpage and to pull in a large amount of data from the db onto one page and not use res.render just for one table.
const { Client } = require('pg');
const client = new Client({
connectionString: process.env.DATABASE_URL,
ssl: true,
});
app.get('/sql',function(req,res){
res.status(200);
client.connect();
client.query('SELECT * FROM people;', (err, res) => {
if (err) throw err;
for (let row of res.rows) {
console.log(JSON.stringify(row));
}
client.end();
})
res.render('pages/sql', {body:"HELLO"});
});
Ideally where it says HELLO, I would be sending the data I get from the stringified JSON
So you have to send the response when you got the result, so you should do something like this
app.get('/sql',function(req,res){
res.status(200);
client.connect();
client.query('SELECT * FROM people;', (err, res) => {
if (err) throw err;
for (let row of res.rows) {
// If you want to get the single row
res.render('pages/sql',JSON.stringify(row));
console.log(JSON.stringify(row));
}
// if you want all rows return then
res.render('pages/sql',JSON.stringify(res.rows));
client.end();
})
});

Can app.render() render an array of views?

It does not seem to be documented, I was wondering if it is possible to render multiple views or an array of views in Expressjs like so:
const data = 'some data passed by a DB';
const app = express();
const arrayViews = ['layout','email', 'web'];
app.render(arrayViews, data, (err, html) => {
if (err) throw err;
})
or do I have to do it in multiple instances
app.render('email', data, (err, html) => {
if (err) throw err;
})
app.render('web', data, (err, html) => {
if (err) throw err;
})
No. You cannot pass an array of views to app.render(). The point of app.render() is to create ONE piece of rendered HTML that can be sent as a response to a particular http request. You can't send multiple responses to one http request. So, you only want to call res.render() once for any given request.
If you want to have two different types of responses for different situations, then you should either make a separate route for each response and call res.render() once in each response with the appropriate template for that request.
Or, you can pass in a query parameter in the URL and use an if statement in the route handler to decide which template to render. But, the point is that you send exactly one response for each request.
For example, here's looking at a query parameter to decide which type of render to do:
app.get('/mypage', function(req, res) {
if (req.query.page === "email") {
res.render('email', data);
} else {
res.render('web', data);
}
});
Otherwise, you'd have two separate routes:
app.get('/mypage/email', function(req, res) {
res.render('email', data);
});
app.get('/mypage/web', function(req, res) {
res.render('web', data);
});
I may have originally been confused about the point of your question (since you don't show the overall context of your render or what you're using it for).
If you're using app.render() to collect the HTML from rendering operations, you can call that multiple times and wait for all to be done and then you have multiple rendered templates which you can do whatever you want with. But, a single app.render() doesn't accept multiple templates. You'd have to call it multiple times and wait for all the requests to be done.
Promise.all([appRender('email'), appRender('web')]).then([email, web] => {
// can access both rendered templates here
// to do with them whatever you want to do
}).catch(err => {
// error here
});
Or, you could use this to make a version of app.render() that would take an array:
const util = require('util');
const appRender = util.promisify(app.render.bind(app));
app.renderAll = function(arrayOfTemplates) {
return Promise.all(arrayOfTemplates.map(template => {
return appRender(template);
}));
});
app.renderAll(['email', ['web']]).then([email, web] => {
// can access both rendered templates here
// to do with them whatever you want to do
}).catch(err => {
// error here
});

Effective way to get data that's needed on all pages

I'm using nodejs and express and I have a navigation menu that is built using data that is in mongodb currently I'm just making a call to the database to get a list of companies and passing that back inside each route. There doesn't seem to be a way to store this information in localstorage client side. So I"m wondering what is the most effective way to handle this situation. Sample of my code
admin.get('/', function(res, resp){
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
res.render('adminview', {companies:companies})//once the page is rendered I would like to save the company list to localstorage.
})
})
})
admin.get('/:company', function(res, resp){
/* repeating code from above because I need this list */
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
/* more code to do stuff to render the company page */
res.render('companyadminview', {companies:companies, company:company})
}) })
I could be going about this the wrong way I'm new to web development this feels wrong to me but can't figure out a different way.
So, first off you should be able to store it in localstorage or sessionstorage just fine, unless you're targeting browsers that don't support it.
That said, I think it's best not to, as the fact that you're storing it in the DB implies that it changes with enough frequency that you will get buggy clientside behavior if you cache it there for too long.
Instead, I'd just setup a middleware and attach it to the locals object on a per request basis, unless you want to do some kind of cache on the server:
app.use(function(req, res, next) {
mongodb.connect(url, function(err, db){
if (err) return next(err);
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
if (err) return next(err);
res.locals.companies = companies;
next();
});
});
});

perform a mongoose query in querystring

I'm trying to do a simple mongoose query using the query string.
This works
router.get('/', function(req,res) {
myModel.find({name:"test e"}, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
This doesn't work (I get the whole collection)..
router.get('/', function(req,res) {
console.log(req.query.q)
myModel.find(req.query.q, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
with this request
/api/myModel?q={name:"test e"}
I don't think it's an url encoding issue since I print the 'q' var and it looks fine server side.
Side question: if this isn't the standard mode, what's the RESTful standard way to query a db?
Edit for more general details:
I don't need a simple access by id or name like Ashley B suggests, I need a proper search engine for my db (the user, using a graphic web interface, have to be able to query each field separately, potentially)
Edit 2:
thanks to Blakes Seven I solved my initial problem, but if you know or use a better way to perform a complex query I would happy to discuss. Maybe I should expose anther resource "/api/seach"?
I think I can answer your first question by answering your second (side question).
Let's say you have a User model, normally you'd have an endpoint to get all users, like so:
router.get('/users', function(req, res) {
// find all users
users.find({}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
To find a specific user you then have an endpoint like so:
router.get('/users/:name', function(req, res) {
// get the user's name from the url and find that user
users.find({name: req.params.name}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
So rather than passing the whole query through the query string, you just find use a specific part. Allowing the users to directly access your data with their own queries makes it much much harder to secure.
I would recommend you to use some library to parse the querystrings to mongoDB queries. It would fix your problem and make your code better.
querymen would help you by transforming /myModel?q=test+e into {name: 'test e'}, giving you full control over the querystring schema.
var querymen = require('querymen')
// querymen.middleware() defines an express middleware with querystring schema
router.get('/', querymen.middleware({
q: {
type: String,
paths: ['name']
}
}), function(req, res) {
console.log(req.querymen.query) // {name: 'test e'}
myModel.find(req.querymen.query, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
The proper query should look like this:
/api/myModels?name=test%20e
The myModals part is in plural.
Check here: How to design RESTful search/filtering?

Nodejs inserting data to mongodb. It takes too much time

Hi i am developing nodejs application. I am inserting data to mongodb but my page always in 'loading' mode. But strange thing is my data inserted to mongodb immediately but page load not stopping. My code is shown below:
app.post('/Management/Post/New',function(req, res){
new Post({
title:req.body.post.title,
body:req.body.post.body,
keywords:req.body.post.keywords
}).save(function (err, docs){
if(err) {
return res.render(__dirname + "/views/createpost", {
title: 'Yeni Gönderi Oluştur',
stylesheet: 'postcreate',
error: 'Gönderi oluşturulurken bir hata ile karşılaşıldı'
});
}
console.log('Gönderi oluşturuldu');
});
});
Have no idea.
You only send a response when there is an error. If there's no error, you server never sends anything back: that's why the page seems to always be loading.
You need to send a response when you have no error, like this:
.save(function (err, docs){
if(err) { // Executed when there was an error with Mongo
return res.render(...);
} else { // Executed when everything is fine
return res.render(...);
}
});
You aren't handling the success scenario except for a console.log. You need a res.render() or res.redirect() on success, not just error

Resources