26

I am toying around with all the fancy node.js/mongodb/express platforms, and stumbled across a problem:

app.get('/tag/:tag', function(req, res){
  var tag=req.params.tag;
  console.log('got tag ' + tag + '.');
  catalog.byTag(tag,function(err,cursor) {
     if(err) {
       console.dir(err);
       res.end(err);
     } else {
       res.writeHead(200, { 'Content-Type': 'application/json'});

       //this crashes
       cursor.stream().pipe(res);

     }
  });
});

As you probably guessed, catalog.byTag(tag, callback) does a find() query to Mongodb and returns the cursor

This leads to an error:

TypeError: first argument must be a string or Buffer

According to mongodb driver doc, I tried to pass this converter to stream():

function(obj) {return JSON.stringify(obj);}

but that does not help.

Can anybody tell me how to correctly stream something to a response?

Or is the only solution a boilerplate to manually pump the data using the 'data' and 'end' events?

2
  • does it work if you replace catalog.byTag with the direct call to MongoDB native driver? Commented Nov 18, 2013 at 23:13
  • No, same error. Also, I can call toArray() and send that via res.json(), which works, but I would prefer streaming instead of buffering on the server and sending it out afterwards. Commented Nov 19, 2013 at 20:12

6 Answers 6

23

Use the cursor stream in combination with JSONStream to pipe it to your response object.

cursor.stream().pipe(JSONStream.stringify()).pipe(res);
Sign up to request clarification or add additional context in comments.

1 Comment

(And make sure you res.set('Content-Type', 'application/json'); before, as your response will otherwise be interpreted as text.)
13

A working combination of other answers here

app.get('/comments', (req, res) => {
  Comment.find()
    .cursor()
    .pipe(JSONStream.stringify())
    .pipe(res.type('json'))
})

http://mongoosejs.com/docs/api.html#query_Query-cursor

  • cursor() returns a Node streams3 compatible stream and is preferred over the deprecated query.stream() interface.
  • Piping to JSONStream.stringify() to combine documents into an array instead of single objects
  • Piping to res.type('json') which sets the HTTP Content-Type header to application/json and returns itself (the response stream) again.

9 Comments

This doesn't produce valid JSON, though, it just concatenates the stringified objects together instead of wrapping it properly in an array.
@robertklep you are right! I did not know that. Thanks!
since we are dealing with express, it seems some error handling is required here. I would like to see some recommended pattern.
@robertklep what's the alternative, for a valid JSON output?
@Tiago the current answer will work (my comment referred to a previous version of the answer that has since been editted and fixed).
|
9

Simple. .stream({transform: JSON.stringify});

Comments

3

Your mongo stream is dumping objects into the res stream which can only handle strings or buffers (hence the error).

Luckily, streams are easy to pipe together so its not too hard to make a transform stream to stringify your data.

in node v0.10.21:

var util = require('util')
var stream = require('stream')
var Transform = stream.Transform

util.inherits(Stringer, Transform)

function Stringer() {
  Transform.call(this, { objectMode: true } )
  // 'object mode allows us to consume one object at a time

}

Stringer.prototype._transform = function(chunk, encoding, cb) {
  var pretty = JSON.stringify(chunk, null, 2) 
  this.push(pretty) // 'push' method sends data down the pike.
  cb() // callback tells the incoming stream we're done processing 
}

var ss = new Stringer()

db.createObjectStreamSomehow()
  .pipe(ss)
  .pipe(res)

hope that helps

Comments

2

Using mongoose and express:

function(req, res){
    var stream = database.tracks.find({}).stream();
    stream.on('data', function (doc) {
        res.write(JSON.stringify(doc));
    });
    stream.on('end', function() {
        res.end();
    });
}

Comments

0

A better and recent solution for this problem:

const { JsonStreamStringify } = require("json-stream-stringify");

app.get("/", (req, res) => {
  const cursor = db.collection("hello").find({}).limit(10).stream();
  x = new JsonStreamStringify(cursor).pipe(res);
  x.on("data", (doc) => {
    res.write(doc);
  });
});

The above solution is using https://www.npmjs.com/package/json-stream-stringify package.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.