Node.js Express app handle startup errors

28,604

Solution 1

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.

Solution 2

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);
};

Solution 3

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)
}
Share:
28,604
Piane_Ramso
Author by

Piane_Ramso

Updated on June 02, 2020

Comments

  • Piane_Ramso
    Piane_Ramso almost 4 years

    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?