Why is my stream error handler not being called? - javascript

I have the following restify handler:
var assert = require('assert-plus');
var request = require('request');
function postParseVideo(req, res, next) {
assert.string(req.body.videoSourceUrl, 'videoSourceUrl');
var stream = request.get({uri: req.body.videoSorceUrl});
stream.on('response', function(parseResponse) {
fnThatTakesAReadableStream(parseResponse, function(err, data) {
if (err) {
console.log(err);
next(err);
} else {
res.send(201, null, {Location: data.location});
next();
}
});
});
stream.on('error', function(err) {
console.log(err);
next(err);
});
};
When I run this, neither of the stream event handlers is ever called. Instead, an error bubbles up to the restify server: {"code":"InternalError","message":"options.uri is a required argument"}. I looked at the source for request v2.54.0, and it looks like that error message must be coming from line 406 of restify.js, which reads:
return self.emit('error', new Error('options.uri is a required argument'))
I have used streams successfully in a number of other languages, but I’m new to using them in JavaScript. It seems to me like request.get is throwing a synchronous error, when it should be emitting an 'error' event. Is there something I’m fundamentally misunderstanding about how streams are implemented in Node?

Your req.body.videoSourceUrl may be defined and of type string, but it's probably empty.
I can reproduce your error by doing:
request.get("")
.on('response', function(){
})
.on('error', function(){
})
I'd also think about dumping assert-plus, as:
var ass = require('assert-plus');
ass.string(new String("yes"), "no");
throws

Related

Express Try and Catch in Form of middleware

i'm working on node.js using Express to built a backend.
i'm intended to handle status 500 error that may happened.
router.put('/test', async (req, res) => {
try {
return res.send(await request.updateTest(req.body, 1))
} catch(err) {
console.log(err)
return res.status(500).send(err.stack)
}
})
this is my example of the code. it's do work perfectly. but when i'm try to make unknown error from the database query, i want to log the error and return status 500 as response with the error detail.
but i'll need to add try and catch every time i'm build a new controller/routes
is there anyway i could express them in form of middleware instead of write try and catch everytime?
this is an example of code i've try to make it as middleware but it's has no work and no effect when called.
error.js
module.exports = function (err, req, res, next) {
console.log(err)
res.status(500).send({
error: 'Internal Server Error',
message: err.stack
})
next(err)
}
main.js
const errorHandler = require('./error')
const { corsOption } = require('./cors')
const cors = require('cors')
const test = require('./test')
module.exports = function (app) {
app.use(cors(corsOption))
app.use(errorHandler)
app.use('/api/test', test)
}
is there anyway that i can do for this to work?
Your global error handler should be placed after all other middlewares/routes:
app.use(middleware)
// all other middlewares
app.use('/api/test', test)
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
To avoid adding try/catch to everything, better to wrap your route handler to catch the errors (asyncWrapper):
app.use(middleware)
// all other middlewares
const asyncWrapper = (cb) => {
return (req, res, next) => cb(req, res, next).catch(next);
};
const test = async (req, res) => {
return res.send(await request.updateTest(req.body, 1))
}
// wrap your handler to catch errors (Async functions return a promise)
app.use('/api/test', asyncWrapper(test))
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
There are two approaches to resolve unhandled exceptions in Node.js
Using try-catch blockwhich is already you are using
Using Process i.e use Process to handle exception. A process is a global object that provides information about the current Node.js process. The process is a listener function that is always listening to the events. The most effective and efficient approach is to use Process. If any uncaught or unhandled exception occurs in your code flow, that exception will be caught in code
process.on('uncaughtException', function(err) {
// Handle the error safely
console.log(err)
})
The above code will be able to handle any sort of unhandled exception which occurs in Node.js. see this Process Events

Koa#2 error handling

I'm building an API with Koa. I have all my routes in place with koa-router. Each route uses a controller that has all the logic for a given mongoose model. I've read the Koa docs on error-handling and understand the use of awaiting in a try/catch block. There they mention a Default Error Handler should be set at the beginning of the middleware chain. So if I was to have something like the following, I should have resonable error handling for the route at router.get():
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
err.status = err.statusCode || err.status || 500;
throw err;
}
});
router
.get('/', async (ctx, next) => {
console.log('Got Route');
//ctx.body = users;
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, () => console.log('Koa app listening on 3000'));
If I was to have something slightly more complex at this route, is there any benefit of adding another try/catch inside the route?
router
.put('/', async function updateOnServer(ctx, next) {
try {
await Model.updateOne({
_id: ctx.params.id,
}, {
field1: ctx.request.body.field1,
$push: { field2: ctx.request.body.field2 },
}).exec();
} catch (e) {
console.log(e);
}
await next();
});
Am I just adding redundant error handling here?
I re-read the docs on Error Handling and also found this little tip on the Koa Wiki. From that, I've concluded the following:
Koa Error Handling states:
However, the default error handler is good enough for most use cases.
The default error handler in this case is the Koa-built-in error handler. You do not need to include any kind of custom error handling in the code you write. Koa will write out a stack trace along with the error message, etc.
If you want to modify how the error is handled, add something like the suggested middleware at the very beginning of the middleware chain:
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
err.status = err.statusCode || err.status || 500;
ctx.body = err.message;
ctx.app.emit('error', err, ctx);
}
});
If you want to change that again for a specific route, or a special bit of logic, then add another try/catch block at that point (as per my above):
router
.put('/', async function updateOnServer(ctx, next) {
try {
await Model.updateOne({
_id: ctx.params.id,
}, {
field1: ctx.request.body.field1,
$push: { field2: ctx.request.body.field2 },
}).exec();
} catch (e) {
ctx.status = 418;
ctx.body = "a custom error message, with nothing really helpful";
}
await next();
});
I was wrong in a comment above - Koa's default error handler doesn't always throw an Internal Server Error. It depends on the type of error you throw. However, I prefer to simply throw strings, and to use JavaScript's very own throw statement. So, I implemented a simple Koa error handler that allows me to do this:
throw `404: User ${id} not found`;
and that results in sending back a status 404 with a message like 'User 5 not found'. Here is my Koa middleware to do that (if you throw something other than a string, or the string isn't of the right format, it falls back to Koa's built-in error handler):
app.use(async (ctx, next) => {
try {
await next();
}
catch (err) {
if (typeof err === 'string') {
let lMatches = err.match(/^(\d{3}):\s*(.*)$/);
if (lMatches) {
ctx.status = Number(lMatches[1]);
ctx.body = lMatches[2];
}
else throw err; // use default error handler
}
else throw err; // use default error handler
}
});

Error Handling in Strongloop Loopback

I know the difference between a callback and a middleware next() function.
If a write a custom remote-method in Loopback, I can easily send errors in callback e.g callback(error,null) but in remote hooks or observers whenever I send error in next() function e.g
var err = new Error('This is error');
next(err)
it always says that Internal server error but it does not tell me what error is.
In order to view error I have to login to server and view logs.
Please tell me how can I send error as a response in next() function so that the on frontend I know what error has occurred.
Maybe use a middleware to hook in:
app.use( function(err,req,res){
res.json(err);
});
(This needs to he the last middleware defined...)
basically you can define callbacks with err and result.
For example in loopback,
if I have a model call "Action" you can simply send err or result to front end using json.
app.get('/your/api/call', function (req, res, next) {
var getTeam = function (cb) {
app.models.Team.find({}, function (err, teams) {
if (err) {
cb(err);
} else {
cb(null, teams);
}
});
};
async.waterfall([
getTeam
], function (err, team, role) {
if (err){
res.send(err); //send error to front end
} else {
res.send(team); //send result to front end
}
});
});
This approach can use with "app.use" function in root level also.

How to have express handle and capture my errors

var database = require('database');
var express = require('express');
var app = express();
var cors = require('cors');
app.use(cors());
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({
extended: false
});
app.post('/dosomething', urlencodedParser, function(req, res) {
if (!req.body.a) {
res.status(500).send(JSON.stringify({
error: 'a not defined'
}));
return;
}
firstAsyncFunction(req.body.a, function(err, result) {
if (err) {
res.status(500).send('firstAsyncFunction was NOT a success!');
} else {
if (result.b) {
secondAsyncFunction(result.b, function(err, data) {
if (err) {
res.status(500).send('secondAsyncFunction was NOT a success!');
return;
}
res.send('EVERYTHING WAS A SUCCESS! ' + data);
});
}
else {
res.status(500).send('result.b is not defined');
}
}
});
});
function firstAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
function secondAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
var server = app.listen(process.env.PORT || 3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = app;
I have here a basic express http server. This server has one route, dosomething, which makes two network calls and tells the user if they were a success or not.
This is my entire webserver (this is a bare bones server of my actual server for example purposes). I am now concerned with this server crashing. Reading the docs for express I see there is a default error handler which will catch errors and prevent the server from crashing (http://expressjs.com/en/guide/error-handling.html). I have added the code:
function defaultErrorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}
app.use(defaultErrorHandler);
This still crashes my server though. For example. I had a problem with my database returning an improper JSON response and inside of my firstAsyncFunction (not shown in the code) I tried to parse the JSON and it caused an error telling me it was improper JSON and the server crashed and was unable to take requests anymore until I restarted it. I would like to avoid this and have the default error handler send out a generic response back to the user when this occurs. I thought if I specified the defaultErrorHandler and put it inside of app.use that it would capture and handle all errors, but this does not seem to be the case? Inside of my async function for example you can see I am looking if an error was returned and if it was I send an error back to the user, but what if some other error occurs, how can I get express to capture and handle this error for me?
The defaultErrorHandler cannot handle exceptions that are thrown inside asynchronous tasks, such as callbacks.
If you define a route like:
app.get('/a', function(req, res) {
throw new Error('Test');
});
An error will be thrown, and in this case defaultErrorHandler will successfully catch it.
If the same exception occurs in an async manner, like so:
app.get('/a', function(req, res) {
setTimeout(function () {
throw new Error('Test');
}, 1000);
});
The server will crush, because the callback is actually in another context, and exceptions thrown by it will now be caught by the original catcher. This is a very difficult issue to deal with when it comes to callback.
There is more than one solution though. A possible solution will be to wrap every function that is prone to throw error with a try catch statement. This is a bit excessive though.
For example:
app.get('/a', function(req, res) {
setTimeout(function () {
try {
var x = JSON.parse('{');
}
catch (err) {
res.send(err.message);
}
}, 1000);
});
A nicer solution:
A nicer solution, would be to use promises instead, if it's possible, then for example you can declare a single errorHandler function like so:
function errorHandler(error, res) {
res.send(error.message);
}
Then, let's say you have to following function with fetches stuff from the database (I used setTimeout to simulate async behavior):
function getStuffFromDb() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("{");
}, 100);
});
}
Notice that this function returns an invalid JSON string. Your route will look something like:
app.get('/a', function(req, res) {
getStuffFromDb()
.then(handleStuffFromDb)
.catch(function (error) { errorHandler(error, res) });
});
function handleStuffFromDb(str) {
return JSON.parse(str);
}
This is a very simplified example, but you can add a lot more functionality to it, and (at least theoretically) have a single catch statement which will prevent your server from crushing.

Catching errors from mongoose queries in express

How can I catch error from mongoose queries. In my routes I got something like this:
// router.js
router.route('/signup')
.post(function(req, res) {
var profile = new Profile(); // create a new instance of the profile model
profile.username = req.body.username;
profile.password = profile.generateHash(req.body.password);
profile.save(function(err) {
if (err) { // (A)
throw new Error('user/create error'));
} else {
res.json(200, { user_token: profile._id, username: profile.username });
}
});
});
and in my app were I set up my routes I got this:
// app.js
var router = require('./app/routes/routes');
// global-error handling middleware
app.use(function(err, req, res, next) {
console.log('Some error is happening.');
res.json(500, {status: 500, message: err.message});
});
If I generate a error so I get to line // (A) in my code above I get a stack trace and node.js exists. I want to catch the error In my error handler. How do I do this?
Well, you are already in the request handler, and you already have access to the error produced while saving the profile object. So, there is no need to throw an exception here. You can already handle the problem.
The most likely scenario here is to send a response to the user indicating that the saving of the profile failed.
function(req, res) {
profile.save(function(err) {
if (err) { // (A)
res.send(500, {message: 'Failed to save profile'}
} else {
res.json(200, { user_token: profile._id, username: profile.username });
}
});
}
And that's it. Your client will receive a 500 status error and this evidently represents a problem that your client will need to deal with, like notifying the user, doing a retry, etc, etc, etc.
you can use Promise-like error handling. mongoose permits to use promises on its methods:
profile.save().then((doc) => {
// if done correctly
}).catch((err) => {
// catch error if occurs
// handle error
});
you can read more about mongoose built-in promises there.

Categories

Resources