34

I have app in Node.js and Express. I need to write tests for it. I have a problem with handling Express app errors. I found this How do I catch node.js/express server errors like EADDRINUSE?, but it doesn't work for me, I don't know why. I want to handle errors, which can occured while expressApp.listen() is executing (EADDRINUSE, EACCES etc.).

express = require('express')
listener = express()

#doesn't work for me
listener.on('uncaughtException', (err) ->
  #do something
)

#doesn't work too
listener.on("error", (err) ->
  #do something
)

#this works, but it caughts all errors in process, I want only in listener
process.on('uncaughtException', (err) ->
  #do something
)

listener.listen(80) #for example 80 to get error

Any ideas?

Community
  • 1
  • 1
Piane_Ramso
  • 343
  • 1
  • 3
  • 7
  • `listener.on 'error', ...` should work. Does it just do the normal stack trace and crash even if that line is there? – loganfsmyth Nov 10 '12 at 22:01
  • Yes, if I do this 'listener.listen(80)' it prints stack trace and crashes. Even with 'listener.on 'error', ...' Maybe error occured in this case is not Express error and this is why it doesn't handle. But it is only assumption. – Piane_Ramso Nov 11 '12 at 22:18

4 Answers4

112

This should do the trick:

listener.listen(80).on('error', function(err) { });

What listener.listen actually does is create a HTTP server and call listen on it:

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};
1ac0
  • 2,875
  • 3
  • 33
  • 47
Marius Tibeica
  • 1,414
  • 2
  • 10
  • 8
  • 21
    It is incredible that this is the most simple and correct answer, and that it has no upvotes ! The trick is to understand that the webServer object is returned by app.listen. I use this: ```app.listen(80, function() { console.log('success'); }).on('error', function(err) { if (err.errno === 'EADDRINUSE') { console.log('port busy'); } else { console.log(err); } }); ``` – david_p Nov 27 '14 at 09:40
  • I don't understand that your code is doing. if you're calling app.listen node already does an http.createServer under the hood I thought or are you basically overriding the native app.listen() method and overriding it with your own code there? – PositiveGuy Apr 08 '16 at 21:48
  • 3
    The first line of code is what should be used, everything else is the explanation of why that works. – Marius Tibeica Apr 13 '16 at 11:45
  • THANK YOU Mariuis Tibeica! I applied your code pattern to my express app and am now catching EADDRINUSE errors whereas before my app just crashed without catching the error in a catch of a try block that does the listen. Adding the listen(port).on('error',...) handler directly to the listen is what did the trick. I documented my code in a separate answer below so that it formats better. – Dennis G Allard Apr 20 '23 at 22:22
29

First off, expressJS does not throw the uncaughtException event, process does, so it's no surprise your code doesn't work.

So use: process.on('uncaughtException',handler) instead.

Next, expressJS already provides a standard means of error handling which is to use the middleware function it provides for this purpose, as in:

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

This function returns an error message to the client, with optional stacktrace, and is documented at connectJS errorHandler.

(Note that errorHandler is actually part of connectJS and is only exposed by expressJS.)

If the behavior the existing errorHandler provides is not sufficient for your needs, its source is located at connectJS's errorHandler middleware and can be easily modified to suit your needs.

Of course, rather than modifying this function directly, the "correct" way to do this is to create your own errorHandler, using the connectJS version as a starting point, as in:

var myErrorHandler = function(err, req, res, next){
    ...
    // note, using the typical middleware pattern, we'd call next() here, but 
    // since this handler is a "provider", i.e. it terminates the request, we 
    // do not.
};

And install it into expressJS as:

app.configure(function(){
    app.use(myErrorHandler);
});

See Just Connect it, Already for an explanation of connectJS's idea of filter and provider middleware and How To Write Middleware for Connect/Express for a well-written tutorial.

You might also find these useful:

Finally, an excellent source of information regarding testing expressJS can be found in its own tests.

Community
  • 1
  • 1
Rob Raisch
  • 17,040
  • 4
  • 48
  • 58
  • Thanks for so detailed explanaition, it would be useful anyway. I don't need to handle errors while web request is handling. I need while create server and call app.listen(...). Now I understand that here Node.js errors handling is needed, not middleware in Express. process.on('uncaughtException',handler) is acceptable in my case. – Piane_Ramso Nov 11 '12 at 22:08
  • @Piane_Ramso, then yes, adding your own event handlers to `process.on('uncaughtException')` is definitely the way to go. If you're curious, the underlying code that processes "uncaught" exceptions in located at (or near) line# 1739 in [node.cc](https://github.com/joyent/node/blob/master/src/node.cc) – Rob Raisch Nov 12 '12 at 22:27
  • christ this is complicated – PositiveGuy Apr 08 '16 at 21:45
15

Mention: Marius Tibeica answer is complete and great, also david_p comment is. As too is Rob Raisch answer (interesting to explore). https://stackoverflow.com/a/27040451/7668448
https://stackoverflow.com/a/13326769/7668448

NOTICE

This first method is a bad one! I leave it as a reference! See the Update section! For good versions! And also for the explanation for why!

 Bad version

For those who will find this useful, here a function to implement busy port handling (if the port is busy, it will try with the next port, until it find a no busy port)

app.portNumber = 4000;
function listen(port) {
    app.portNumber = port;
    app.listen(port, () => {
        console.log("server is running on port :" + app.portNumber);
    }).on('error', function (err) {
        if(err.errno === 'EADDRINUSE') {
            console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
            listen(port + 1)
        } else {
            console.log(err);
        }
    });
}

listen(app.portNumber);

The function listen is recursively calling itself. In case of port busy error. Incrementing the port number each time.

update Completely re-done

 Callback full version

First of all this version is the one that follow the same signature as nodejs http.Server.listen() method!

function listen(server) {
    const args = Array.from(arguments);
    // __________________________________ overriding the callback method (closure to pass port)
    const lastArgIndex = arguments.length - 1;
    let port = args[1];
    if (typeof args[lastArgIndex] === 'function') {
        const callback = args[lastArgIndex];

        args[lastArgIndex] = function () {
            callback(port);
        }
    }

    const serverInstance = server.listen.apply(server, args.slice(1))
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

Signature:

listen(serverOrExpressApp, [port[, host[, backlog]]][, callback])

just as per

https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback

The callback signature is changed to

(port) => void

 usage:

const server = listen(app, 3000, (port) => {
    console.log("server is running on port :" + port);
});

// _____________ another example port and host
const server = listen(app, 3000, 'localhost', (port) => {
    console.log("server is running on port :" + port);
});

 Explanation

In contrary to the old example! This method doesn't call itself!

Key elements:

  • app.listen() first call will return a net.Server instance
  • After binding an event once, calling listen again into the same net.Server instance will attempt reconnecting!
  • The error event listener is always there!
  • each time an error happen we re-attempt again.
  • the port variable play on the closure to the callback! when the callback will be called the right value will be passed.

Importantly

serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));

Why we are skipping the callback here!?

The callback once added! It's hold in the server instance internally on an array! If we add another! We will have multiple triggers! On the number of (attempts + 1). So we only include it in the first attempt!

That way we can have the server instance directly returned! And keep using it to attempt! And it's done cleanly!

Simple version port only

That's too can help to understand better at a glimpse

function listen(server, port, callback) {
    const serverInstance = server.listen(port, () => { callback(port) })
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen(port);
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

Here the parameter port variable play on the closure!

ES6 full version

function listen(server, ...args) {
    // __________________________________ overriding the callback method (closure to pass port)
    const lastArgIndex = args.length - 1;
    let port = args[0];
    if (typeof args[lastArgIndex] === 'function') {
        const callback = args[lastArgIndex];

        args[lastArgIndex] = function () {
            callback(port);
        }
    }

    const serverInstance = server.listen(server, ...args)
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen(...[port, ...args.slice(1, lastArgIndex)])
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

Why the old version is bad

To say right it's not really! But with the first version! We call the function itself at every failure! And each time it create a new instance! The garbage collector will budge some muscles!

It doesn't matter because this function only execute once and at start!

The old version didn't return the server instance!

Extra (for @sakib11)

You can look at @sakib11 comment to see what problem he fall in! It can be thoughtful!

Also in the comment i mentioned promise version and closure getter pattern! I don't deem them interesting! The way above just respect the same signature as nodejs! And too callback just do fine! And we are getting our server reference write away! With a promise version! A promise get returned and at resolution we pass all the elements! serverInstance + port!

And if you wonder for the closure getter pattern! (It's bad here)

Within our method we create a ref that reference the server instance! If we couldn't return the server instance as we are doing (imaging it was impossible! So each time a new instance is created! The pattern consist of creating a closure (method at that scope) And return it!

so for usage

const getServer = listen(port, () => {
   console.log('Server running at port ' + getServer().address().port);
   const io = socketIo(getServer(), {});
}); 

But it's just overhead specially we need to wait for the server to be done! Unless we set it in a way that it use a callback! or return a promise!

And it's just over complicating! And not good at all!

It's just because i mentioned it!

And the method above can be tweaked! To add number of attempts limit! And add some events or hooks! But well! Generally we only need a simple function that just attempt and make it! For me the above is more then sufficient!

Good links

From the doc

The app.listen() method returns an http.Server object and (for HTTP) is a convenience method for the following:

app.listen = function () {
  var server = http.createServer(this)
  return server.listen.apply(server, arguments)
}
Community
  • 1
  • 1
Mohamed Allal
  • 17,920
  • 5
  • 94
  • 97
  • That perfect solution for my problem, thank you bro – Hiep Tran Jul 27 '19 at 03:47
  • Glade to hear that. My pleasure. Thank you – Mohamed Allal Jul 27 '19 at 11:53
  • i was trying something similar and the recursive bit works exactly like this but i was also trying to return the app.listen to pass it to socket.io and for some reason it would not work. I guess attaching the error event handler returns something different? – sakib11 Jun 01 '20 at 13:47
  • How did you return the value! In my example if we return the first app.listen().on() we will get a server instance! But if it fails! the callback one of retry will be call! And a new server instance will be created! So that should be your problem! app.listen and all the .on chaining all will return a net.Server instance! Which you can attach to socket.io! You have mainly two options! Use http.createServer. get your server instance! Then in my function in place of app.listen use server.listen! And attach it to socket.io! You have the reference! – Mohamed Allal Jun 02 '20 at 04:07
  • Otherwise if we want to do it within the function itself! then we have many options! Using an onSuccess callback Or promises can be the simplest! You pass the successfull attached server at resolve! – Mohamed Allal Jun 02 '20 at 04:12
  • Just for benefit ! another pattern is to return a closure getter that return the server reference! I updated the answer for examples – Mohamed Allal Jun 02 '20 at 04:19
  • Or even better! We can save the first attempt server reference! And use it for the following calls! Check the example on the answer – Mohamed Allal Jun 02 '20 at 04:22
  • I went through different things! check the answer it will hold all! There is a lot to say – Mohamed Allal Jun 02 '20 at 05:42
0

Thank you Marius Tibeica!

var app = express();
const port = process.env.PORT || 3000

My original code DOES NOT catch EADDRINUSE error:

try {
    app.listen(port)
    console.log('Listening on port ' + port)
} catch (err) {
    console.log('ERROR: ' + err)
    console.log('Exiting...')
}

My code improved per Marius' suggestion DOES catch EADDRINUSE error:

try {
    app.listen(port).on('error', (err) => {
        console.log('ERROR: ' + err)
        console.log('Exiting...')
    })
    console.log('Listening on port ' + port)
} catch (err) {
    console.log('ERROR: ' + err)
    console.log('Exiting...')
}