AssertionError [ERR_ASSERTION]: handler (func) is required in mongodb - javascript

I am using mongooose to connect mongodb but i am getting following error
/Users/uchitkumar/api/node_modules/mongodb/lib/mongo_client.js:804
throw err;
^
AssertionError [ERR_ASSERTION]: handler (func) is required
at new AssertionError (internal/errors.js:315:11)
at _toss (/Users/uchitkumar/api/node_modules/assert-plus/assert.js:22:11)
at Function.out.(anonymous function) [as func] (/Users/uchitkumar/api/node_modules/assert-plus/assert.js:122:17)
at process (/Users/uchitkumar/api/node_modules/restify/lib/server.js:1352:20)
at argumentsToChain (/Users/uchitkumar/api/node_modules/restify/lib/server.js:1361:12)
at Server.serverMethod [as put] (/Users/uchitkumar/api/node_modules/restify/lib/server.js:1475:21)
my code for connection is as follow
server.listen(config.port, function() {
mongoose.connection.on('error', function(err) {
console.log('Mongoose default connection error: ' + err)
process.exit(1)
})
mongoose.connection.on('open', function(err) {
if (err) {
console.log('Mongoose default connection error: ' + err)
process.exit(1)
}
console.log(
'%s v%s ready to accept connections on port %s in %s environment.',
server.name,
config.version,
config.port,
config.env
)
require('./routes')
})
global.db = mongoose.connect(config.db.uri)
})
routes code
server.get('/', function indexHTML(req, res, next) {
fs.readFile(__dirname + '/../index.html', function (err, data) {
if (err) {
next(err);
return;
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end(data);
next();
});
});
This was fine ... I changed something and now it stopped working with this error. The error is that it is not able to assert some function... in mongodb client. it needed a function. Is it asking to add some handler function? where to add that
Thank in advance

handler (func) is required is an error that is thrown by restify if one of your routes or middlewares is undefined.
For example:
server.put('/foo/');
This would also trigger it:
var myMidelware = undefined; // todo: define this
app.put('/route', myMiddleware, (req, res) => { /* todo: handle req */ })
That will throw the error handler (func) is required when it tries to validate that myMidelware is a function.
I don't see that in your posted routes code, but I think it's happening somehow. Do you have a PUT method defined somewhere?
(The same error would also happen with server.get(), server.post(), etc, but the [as put] in the stack trace indicates that it's choking on a server.put() call.)
See https://github.com/restify/node-restify/blob/v7.2.1/lib/server.js#L1386
Also, I don't believe the error has anything to do with mongodb; mongo is just in the stack because you run require('./routes') in the mongo connection open handler. The error is coming from your routes file. Annoyingly, mongo's error handling is loosing part of the stack trace. If you moved require('./routes') to outside of the mongo stuff, it would give you the proper stack trace.

Related

Error cannot be caught with Express.js and gridfs-stream

It's an easy image (picture) download server, Express.js receives the request, gets an image from MongoDB GridFS, and responds with the file.
It's OK when request is valid (when the requested file exists).
The problem is that I cannot catch the MongoError when the query failed (i.e. requested image does not exist).
import Grid from 'gridfs-stream'
const root = 'fs_images'
// This func returns the file stream
export function getReadStream (id) {
const gfs = Grid(mongoose.connection.db, mongoose.mongo)
const options = {
_id: id,
mode: 'r',
root: root
}
const readStream = gfs.createReadStream(options)
readStream.on('error', function (err) {
// throw here
// it performs the same without this on-error hook;
// if comment the `throw err`, nothing will happens
// but I want the caller knows the error
throw err
})
return readStream
}
And this is the router
router.get('/:fileId', function (req, res, next) {
const fileId = req.params.fileId
try {
const imgReadStream = image.getReadStream(fileId)
imgReadStream.pipe(res)
} catch (err) {
// nothing catched here
// instead, the process just crashed
console.log(err)
}
}
And I just cannot catch the err. When I try to request something that doesn't exist the MongoError shows in the console, and the app crashes with errno is 1.
Head of console output:
/.../node_modules/mongodb/lib/utils.js:123
process.nextTick(function() { throw err; });
^
MongoError: file with id 123456123456123456123456 not opened for writing
at Function.MongoError.create (/.../node_modules/mongodb-core/lib/error.js:31:11)
This may be a bit different. If somewhere else throws an Error it will be caught by my error-handler (app.use(function(err, req, res, next){ /* ... */})), or at least by the default handler from Express.js, and returns a 500, without the process crashing.
In short, I want the app to know and catch this MongoError so I can handle it manually (i.e. return a 404 response).
try/catch won't work because the error is happening in a different tick (asynchronously). Perhaps you could listen for the error in the router instead?
const imgReadStream = image.getReadStream(fileId)
imgReadStream.on('error', function(err) {
// Handle error
});
imgReadStream.pipe(res)

How to throw a 404 error in express.js?

In app.js, I have
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
so if I request some not exist url like http://localhost/notfound, above code will execute.
In exist url like http://localhost/posts/:postId, I would like to throw 404 error when access some not exist postId or deleted postId.
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
// How to throw a 404 error, so code can jump to above 404 catch?
}
In Express, a 404 isn't classed as an 'error', so to speak - the reasoning behind this is that a 404 isn't usually a sign that's something's gone wrong, it's just that the server couldn't find anything. Your best bet is to explicitly send a 404 in your route handler:
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
res.status(404).send("Not found.");
}
Or alternatively, if this feels like too much repeated code, you could always pull that code out into a function:
function notFound(res) {
res.status(404).send("Not found.");
}
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
notFound(res);
}
I wouldn't recommend using a middleware in this situation solely because I feel like it makes the code less clear - the 404 is the direct result of the database code not finding anything, so it makes sense to have the response in the route handler.
I have the same app.js structure, and I solved this problem in this way in the route handler:
router.get('/something/:postId', function(req, res, next){
// ...
if (!post){
next();
return;
}
res.send('Post exists!'); // display post somehow
});
The next() function will call the next middleware which is the error404 handler if it is right after your routes in the app.js.
You can use this and the end of your routers.
app.use('/', my_router);
....
app.use('/', my_router);
app.use(function(req, res, next) {
res.status(404).render('error/404.html');
});
you're probably looking for something like https://github.com/expressjs/api-error-handler
or just https://github.com/jshttp/http-errors
Even though 404 pages are not considered an error in Express as written here, its really damn handy if you DO handle them like so. For instance when you are developing an API that wants consistent JSON output. The following code should help you with that:
Define a helper function abort to create status errors that can be easily used in your code to pass to the next function:
// Use the `statuses` package which is also a dependency of Express.
const status = require('statuses');
const abort = (code) => {
const err = new Error(status[code]);
const err.status = code;
return err;
};
Define the catch-all middleware for 404 pages which should be defined at the bottom of your stack (after all routes have been added). This forwards the 404 as an error:
app.use((req, res, next) => {
next(abort(404));
});
Lastly, the final error handler will now consistently send all errors in JSON format:
app.use((err, req, res, next) => {
if(!res.headersSent) {
// You can define production mode here so that the stack trace will not be sent.
const isProd = false;
res.status(err.status || 500).json({
error: err.toString(),
...(!isProd && {stack: err.stack.split('\n').map(i => i.trim())}),
});
}
next(err);
});

Node.js, error 344, can't set headers after they are set

I'm new to Node. Trying to wrap my head around this error I get, I don't understand why. I seen a couple of posts with it, but they are all different cases and none kinda match mine. So appreciate help. From what I seen in other posts It's most cases in a post, mine is a get and I do return res on error.
GET /posts/56f2a808653270ff11f7ddb2 304 30.391 ms - -
_http_outgoing.js:344
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
at ServerResponse.header (/Users/me/nodyhead/node_modules/express/lib/response.js:718:10)
at ServerResponse.json (/Users/me/nodyhead/node_modules/express/lib/response.js:246:10)
at /Users/me/nodyhead/routes/index.js:50:13
at next (/Users/me/nodyhead/node_modules/mongoose/lib/model.js:2491:5)
at EventEmitter.<anonymous> (/Users/me/nodyhead/node_modules/mongoose/lib/model.js:2621:5)
at EventEmitter.<anonymous> (/Users/me/nodyhead/node_modules/mpromise/lib/promise.js:180:45)
at emitThree (events.js:97:13)
at EventEmitter.emit (events.js:175:7)
at Promise.safeEmit (/Users/me/nodyhead/node_modules/mpromise/lib/promise.js:83:21)
at Promise.fulfill (/Users/me/nodyhead/node_modules/mpromise/lib/promise.js:104:24)
at Promise.resolve (/Users/me/nodyhead/node_modules/mongoose/lib/promise.js:142:23)
at next (/Users/me/nodyhead/node_modules/mongoose/lib/model.js:2687:15)
at /Users/me/nodyhead/node_modules/kareem/index.js:177:19
at /Users/me/nodyhead/node_modules/kareem/index.js:109:16
at nextTickCallbackWith0Args (node.js:420:9) [nodemon] app crashed - waiting for file changes before starting...
This is my three functions it complains about
router.get('/posts', function(req, res, next) {
Post.find(function(err, posts) {
if (err) { return next(err); }
res.json(posts);
});
});
router.param('post', function(req, res, next, id) {
var query = Post.findById(id);
query.exec(function(err, post) {
if (err) { return next(err); }
if (!post) { return next(new Error('can\'t find post')); }
req.post = post;
return next();
});
});
router.get('/posts/:post', function(req, res) {
req.post.populate('comments', function(err, post) {
if (err) { return next(err); }
res.json(post);
});
res.json(req.post);
});
Well, the issue is that you call res.json() twice. See answer from this topic Error: Can't set headers after they are sent to the client for more information
At your GET posts/:post (your last handler) remove one of the res.json(obj) lines. The point here is that your are responding twice: one before the callback execution and one after the DB access. Keep just one of them.

Centralized error handling for Resitify

I'm having trouble getting centralized error handling set up in my restify app. I'd like to trap certain Mongo errors, such as "E11000 duplicate key error" and then map them to a restify ConflictError.
If I just let the error bubble up from my Mongo call in a route, the client gets a 500 error.
I figured I should trap InternalServerError, but the below handler never gets called:
app.on('InternalServerError', function (req, res, err, cb) {
console.log('++++++++++++++++', err);
return cb(err);
});
I thought I could just use the express approach:
app.use(function (err, req, res, next){...
But restify handlers don't seem to take an error argument. I'm stumped after searching all the usual places. It seems my first approach should have just worked.
This might work for you. Set up a bunyan logger in your app.js file…
var bunyan = require('bunyan');
var log = new bunyan({
name: 'my_api',
streams: [
{
path: './error.log',
level: 'warn'
}
],
serializers: {req: restify.bunyan.serializers.req},
src: false
});
var server = restify.createServer({
log: log
});
Then in your controller do something like this….
var restify = require('restify');
try {
Model.findAll().then(function(vals){
res.send(vals);
next();
});
}
catch(e) {
req.log.error({req_id: req.id()}, 'Error attempting find.');
res.send(409, new restify.ConflictError("Problem executing search."));
next();
}

How to properly handle errors in Express?

I am beginning to work with Express JS and have run into an issue. I can't seem to figure out the proper way to handle errors.
For example, I have a web services API that serves an object called "event". I'd like to return a simple string of "cannot find event" when a user submits an event id that isn't found. Here is how I'm currently structuring my code:
app.get('/event/:id', function(req, res, next) {
if (req.params.id != 1) {
next(new Error('cannot find event ' + req.params.id));
}
req.send('event found!');
});
When I submit an id other than 1, Node crashes with the following output:
http.js:527
throw new Error("Can't set headers after they are sent.");
^
Error: Can't set headers after they are sent.
at ServerResponse.<anonymous> (http.js:527:11)
at ServerResponse.setHeader (/usr/local/kayak/node_modules/express/node_modules/connect/lib/patch.js:62:20)
at /usr/local/kayak/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:72:19
at [object Object].<anonymous> (fs.js:107:5)
at [object Object].emit (events.js:61:17)
at afterRead (fs.js:878:12)
at wrapper (fs.js:245:17)
From what I can tell by using the node.js debugger, execution of the block of code continues after next() is called, meaning that req.send('event found!') tries to run. I don't want this to happen.
The only workaround that I've found is to simply throw a new Error() instead of "next-ing" it, but this results in a default Express HTML error page being generated. I'd like a little more control than that.
I have taken the time to read over the error handling section of the Express documentation, but I couldn't make sense of it.
You'll want to check out Express Error Handling. From there:
app.param('userId', function(req, res, next, id) {
User.get(id, function(err, user) {
if (err) return next(err);
if (!user) return next(new Error('failed to find user'));
req.user = user;
next();
});
});
The sweetspot that you are missing is the return next(...)
That's because you're doing it wrong: you already threw an Error (which will be processed by Express and return a 500 - Error page for the user or something like that) but you are also trying to send your own response to the client: res.send('event found!');
You should really check out the Express guide about Error Handling here: http://expressjs.com/guide/error-handling.html
What I would do in your example is:
function NotFound(msg){
this.name = 'NotFound';
Error.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
}
app.get('/event/:id', function(req, res, next){
if (req.params.id != 1) {
throw new NotFound('Cannot find event ' + req.params.id);
} else {
res.send('event found!');
}
});
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.ejs');
} else {
next(err);
}
});
You have a couple of problems in your code:
When responding to the client, you need to use the response object (res rather than req).
When sending an error to next, you should return, so the rest of the function doesn't run.
Here's your code after fixing those errors:
app.get('/event/:id', function(req, res, next) {
if (req.params.id != 1) {
return next(new Error('cannot find event ' + req.params.id));
}
res.send('event found!'); // use res.send (NOT req.send)
});

Categories