As you hint in your answer, part of the problem is the speed of the template engine itself; You found out that Jade is not the fastest - in fact, it is one of the slowest .
My favorite doT engine. In the performance test I am associated with, doT can display a template 5.4 million times per second. Jade can display a similar pattern at just 29,000 times per second. This is not even a competition.
However, the engine speed pattern is only a small part of the problem here. I believe that your real problem is that the Mongolian driver you are using is poorly designed for the asynchronous Node model. (Disclaimer: I never used Mongous, I just spent a few minutes looking at the code.)
Node is designed to work with data streams. In other words, you have to work on very small pieces of data at a time. In contrast, it seems that Mongous processes the entire data set and returns it to your code as a single JSON object.
This is convenient and great for small data sets, but it completely falls apart when working with large volumes of data such as you (10,000 records). Node will be completely blocked when analyzing and processing a large amount of data (which is very, very bad, because it will not be able to process any incoming connections), and the V8 memory management system is not optimized for large heap allocations like this.
To work with large datasets correctly, you need to use the Mongo driver, which transfers entries to your code, such as node-mongodb-native, or mongoskin , which simplifies the work with the API.
The maerics answer was on the right track, but it was wrong because it uses toArray , which poses the same problem as Mongous: the entire dataset maps to an array in memory. Instead, just use the each cursor method, which asynchronously calls your callback function for each returned record as it arrives. Thus, the entire result set is never in memory; you only work one at a time, allowing you to record records that you have already processed, and, if necessary, collect garbage.
Now that we have established how to get data from the database, we need to figure out how to get it for the client.
The problem is that the Express viewer expects that all your data will be accessible from the front so that the template engine can display a single line for sending to the client. As we discussed above, this is not such a good idea if you are dealing with thousands of records. The right way to do this is to transfer the data that we receive from Mongo directly to the client. Unfortunately, we cannot do this in Express view - they are not intended to be asynchronous.
Instead, you have to write a custom handler. You are already on this path with Hippo's answer and your own attempt, but you really need to use res.write() , not res.send . Like res.render , res.send expects you to get a complete response when you call it, because it internally calls res.end , ending the HTTP response. In contrast, res.write simply sends data over the network, leaving the HTTP response open and ready to send more data; in other words, the flow of your response. (Note that you must set the HTTP headers before starting streaming. For example, res.contentType('text/html'); )
Just because you manually process the response (the rendering system described above) does not prevent you from using the template engine. You can use the template for the header and footer of your document and one for each entry. Let use doT to put everything together.
First, declare our templates. In real life, you can download them from files (or even hack Express to download them for you as views and get the source of the template), but we will just declare them in our code.
var header = doT.template('<!DOCTYPE html><html><head><title>{{=it.title}}</title></head><body>'); var record = doT.template('<p>{{=it.fieldname}}, ...</p>'); var footer = doT.template('</body></html>');
( doT.template returns a function that generates HTML from the above template, and an object that you pass to this returned function when it is called. For example, now we can call header({title:'Test'}); )
Now we are doing the real job in the request handler: (suppose we already have a collection from the Mongo driver)
app.get('/', function(req, res){ res.contentType('text/html'); res.write(header({title:'Test'})); collection.find({}).each(function(err, doc) { if (err) return res.end('error! ' + err);