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.
Related
I'm new to Node and have previously just written Javascript for simple browser extensions. What I'm trying to do is run a shell script and then return the text for it on a get request (simplified version).
I've tried looking at callbacks and can't seem to get my head around it or even adapt another example to what I'm trying to do. My main problem is either that the I'm receiving the error "first argument must be one of type string or buffer. received type undefined" or "received type function" (when I tried to implement a callback, which is what I believe I need to do here?).
I've looked at a few examples of callbacks and promises and seeing them in abstraction (or other contexts) just isn't making sense to me so was hoping someone could help direct me in the right direction?
The code is very crude, but just trying to get some basic functionality before expanding it any further.
var express = require("express");
var app = express();
const { exec } = require("child_process");
var ifcfg = function(callback) {
exec("ifconfig", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return error;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return err;
} else {
var output = stdout.toString();
return callback(output);
}
});
}
app.get("/ifconfig", (req, res) => res.write(ifcfg(data)));
var port = process.env.PORT || 8080;
app.listen(port, function() {
console.log("Listening on " + port);
});
In JavaScript, a callback is a function passed into another function as an argument to be executed later.
Since the command is executed asynchronously you will want to use a callback to handle the return value once the command has finished executing:
var express = require("express");
var app = express();
const { exec } = require("child_process");
function os_func() {
this.execCommand = function(cmd, callback) {
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
callback(stdout);
});
}
}
app.get("/", (req, res) => {
console.log("InsideGetss");
var os = new os_func();
os.execCommand('ifconfig', function (returnvalue) {
res.end(returnvalue)
});
});
I have been having this issue. It seems like it works for a while and then breaks without much rhyme or reason to it.
router.get('/home/', function(req, res, next) {
var code = req.query.code;
console.log("acquired code from SC");
req.SC.authorize(code, function(err, accessToken) {
if ( err ) {
throw err;
} else {
console.log("traded code for access token");
req.session.oauth_token = accessToken;
// Client is now authorized and able to make API calls
//res.render('home', { token: accessToken });
soundcloud.getLoggedInUser(accessToken, function(user){
console.log("done getting user from SC");
});
}
});
});
Here is the getLoggedInUser function.
//get data for the user who is logged in
function getLoggedInUser(accessToken, done){
var href = 'https://api.soundcloud.com/me?oauth_token=' + accessToken;
getRequest(href, done);
}
//used for get requests to soundcloud API
function getRequest(href, done){
console.log(href);
requestify.get(href).then(function(response){
console.log(done);
done(response.getBody());
});
}
Here is the output.
acquired code from SC
traded code for access token
https://api.soundcloud.com/me?oauth_token=
[Function]
I'm guessing this is a problem with my node / express setup rather than a problem with this code itself. Any ideas?
In my app.js file I had the code below and it worked as intended. I need to clean up my code, so I moved it to it's own route at routes/random and it no longer works because I get an error that states: "http://localhost:1337/random/1/1/testing 404 (Not Found)" and I am not sure why. My original code was in my app.js file when it was working was:
app.get('/random/:room/:userId/:message', function(req, res) {
fs.appendFile(room.number.toString(), req.params.message, function(err) {
if (err) {
console.log('error writing messages to file');
};
fs.readFile('./' + room.number, 'utf-8', function(err, data) {
if (err) {
if (err.fileNotFound) {
return this.sendErrorMessage('can\'t find the file, you linked it incorrectly');
}
console.log('error reading message file');
};
if (req.params.userId == 1) {
messages.user1.push(data);
} else {
messages.user2.push(data);
};
console.log(messages);
res.send(data);
fs.unlink(req.params.room, function(err) {
});
});
});
});
the new code includes the following for app.js
var express = require('express');
var app = express();
var fs = require('fs');
var random = require('./routes/random');
app.use('/random/', random);
app.use(express.static('public'));
app.use(express.static('public/js'));
app.use(express.static('public/images'));
and after I moved it, the route code is:
var express = require ('express');
var fs = require ('fs');
var random = express.Router();
random.get('/random/:room/:userId/:message', function(req, res) {
fs.appendFile(room.number.toString(), req.params.message, function(err) {
if (err) {
console.log('error writing messages to file');
};
fs.readFile('./' + room.number, 'utf-8', function(err, data) {
if (err) {
if (err.fileNotFound) {
return this.sendErrorMessage('can\'t find the file, you linked it incorrectly');
}
console.log('error reading message file');
};
if (req.params.userId == 1) {
messages.user1.push(data);
} else {
messages.user2.push(data);
};
console.log(messages);
res.send(data);
fs.unlink(req.params.room, function(err) {
});
});
});
});
module.exports = random;
Can anyone explain what I have done wrong that won't allow it to find the file?
In your code you are defining a route called random\random... in random.js, delete first random there, because middleware(app.use..) will direct all routes with /random to your router instance.
Your router is handling a url that starts with /random, and you attach this to your app under the path /random/. Remove one or the other (preferebly, the one inside the router).
I was able to fix it. Both comments above are correct, but it still was throwing errors due to my variables being undefined. I figured it out though so I am closing this question. Thank you both
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
I've built a series of database queries in my express app that reside in a /models/index.js file which I can access from app.js via var express = require('express');. I am trying to populate req.session.user with a userid that is returned by a findByEmail(); function in /models/index.js.
The findByEmail(); function works fine, however I can't figure out how to store its return value in req.session. I've tried including req.session.id = result.rows[0].id; in the 'findByEmail();function, but this returns areq is not defined` error.
Am I overlooking a simple require statement in my /models/index.js file or is there another trick to accessing req.session in a module?
I've included the relevant code from /models.index.js below:
/models.index.js:
var pg = require('pg');
function findByEmail(email){
pg.connect(function(err, client, done) {
if(err) {
console.log('pg.connect error');
throw err;
}
client.query('BEGIN', function(err) {
if(err) {
console.log('client.query BEGIN error');
return rollback(client, done);
}
process.nextTick(function() {
var text = "SELECT * FROM users WHERE email = $1";
client.query(text, [email], function(err, result) {
if(err) {
console.log(err);
return rollback(client, done);
}
console.log(result);
console.log(result.rows);
console.log('id: ', result.rows[0].id);
req.session.id = result.rows[0].id;
done();
});
});
});
});
}
module.exports.pg = pg;
exports.findByEmail = findByEmail;
As far as /models/index.js knows, req is not defined, same thing with rollback. A module is a closure and you don't have access to variables defined outside of it.
If you want to do that you must pass them as parameters but it's not very good design, as #gustavohenke said: Separation of concerns.
You might want to have a callback and call it with success/error and set the session id there so you don't have to pass in into the module:
function findByEmail(email,callback){
pg.connect(function(err, client, done) {
if(err) {
console.log('pg.connect error');
throw err;
}
// Do all the async work and when you are done ...
// An error is usually passed as the first parameter of the callback
callback(err,result)
});
}
exports.findByEmail = findByEmail;
You would then call it like this:
var models = require('./models');
models.findByEmail('thedude#lebowski.com',function(err,results) {
// set session id here where you probably have access to the req object...
})