118

I need to close server after getting callback from /auth/github/callback url. With usual HTTP API closing server is currently supporting with server.close([callback]) API function, but with node-express server i’m getting TypeError: Object function app(req, res){ app.handle(req, res); } has no method 'close' error. And I don't know how to find information to solve this problem.
How should I close express server?

NodeJS configuration notes:

$ node --version
v0.8.17
$ npm --version
1.2.0
$ npm view express version
3.0.6

Actual application code:

var app = express();

// configure Express
app.configure(function() {
    // … configuration
});

app.get(
    '/auth/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
        res.redirect('/');

        setTimeout(function () {
            app.close();
            // TypeError: Object function app(req, res){ app.handle(req, res); } has no method 'close'
        }, 3000)
    }
);

app.listen('http://localhost:5000/');

Also, I have found ‘nodejs express close…’ but I don't sure if I can use it with code I have: var app = express();.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Vladimir Starkov
  • 19,264
  • 8
  • 60
  • 114

7 Answers7

146

app.listen() returns http.Server. You should invoke close() on that instance and not on app instance.

Ex.

app.get(
    '/auth/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
        res.redirect('/');

        setTimeout(function () {
            server.close();
            // ^^^^^^^^^^^
        }, 3000)
    }
);

var server = app.listen('http://localhost:5000/');
// ^^^^^^^^^^

You can inspect sources: /node_modules/express/lib/application.js

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Vladimir Kuznetsov
  • 1,781
  • 1
  • 12
  • 12
61

In express v3 they removed this function.

You can still achieve the same by assigning the result of app.listen() function and apply close on it:

var server = app.listen(3000);
server.close((err) => {
  console.log('server closed')
  process.exit(err ? 1 : 0)
})

https://github.com/visionmedia/express/issues/1366

João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
Michael
  • 22,196
  • 33
  • 132
  • 187
14

If any error occurs in your express app then you must have to close the server and you can do that like below-

var app = express();
var server = app.listen(process.env.PORT || 5000)

If any error occurs then our application will get a signal named SIGTERM. You can read more SIGTERM here - https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html

process.on('SIGTERM', () => {
  console.info('SIGTERM signal received.');
  console.log('Closing http server.');
  server.close((err) => {
    console.log('Http server closed.');
    process.exit(err ? 1 : 0);
  });
});
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
Neha Sharma
  • 449
  • 5
  • 13
5

I have answered a variation of "how to terminate a HTTP server" many times on different support channels. Unfortunately, I couldn't recommend any of the existing libraries because they are lacking in one or another way. I have since put together a package that (I believe) is handling all the cases expected of graceful express.js HTTP(S) server termination.

https://github.com/gajus/http-terminator

The main benefit of http-terminator is that:

  • it does not monkey-patch Node.js API
  • it immediately destroys all sockets without an attached HTTP request
  • it allows graceful timeout to sockets with ongoing HTTP requests
  • it properly handles HTTPS connections
  • it informs connections using keep-alive that server is shutting down by setting a connection: close header
  • it does not terminate the Node.js process
Gajus
  • 69,002
  • 70
  • 275
  • 438
2

calling server.close does the job

server.close((err) => {
  console.log('server closed')
  process.exit(err ? 1 : 0)
})

also it is good to listen for system(user) signals and shutdown gracefully on them too, for that you should listen on both SIGTERM and SIGINT

const port = process.env.PORT || 5000;
const server = app.listen(port);
console.log(`listening on port:${port}`);
for (let signal of ["SIGTERM", "SIGINT"])
    process.on(signal, () => {
        console.info(`${signal} signal received.`);
        console.log("Closing http server.");
        server.close((err) => {
            console.log("Http server closed.");
            process.exit(err ? 1 : 0);
        });
    });

Ali80
  • 6,333
  • 2
  • 43
  • 33
2

Old question but now Node v18.2.0 introduced server.closeAllConnections(). It should be noted that server.close never runs its callback when the browser sends the request Connection: keep-alive, because server.close only stops the server from accepting new connections, it does not close old connections.

Before Node v18.2.0 I tackled this problem by waiting 5 seconds for the server to shutdown, after which it would force exit.

This code encompasses both situations

process.on('SIGINT', gracefulShutdown)
process.on('SIGTERM', gracefulShutdown)

function gracefulShutdown (signal) {
  if (signal) console.log(`\nReceived signal ${signal}`)
  console.log('Gracefully closing http server')

  try {
    server.close(function (err) {
      if (err) {
        console.error('There was an error', err.message)
        process.exit(1)
      } else {
        console.log('http server closed successfully. Exiting!')
        process.exit(0)
      }
    })

    // closeAllConnections() is only available from Node v18.02
    if (server.closeAllConnections) server.closeAllConnections()
    else setTimeout(() => process.exit(0), 5000)

  } catch (err) {
    console.error('There was an error', err.message)
    setTimeout(() => process.exit(1), 500)
  }
}
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
  • i think closeAllConnections() has to be called AFTER calling close(). – Welcor May 14 '23 at 17:00
  • @Welcor why do you say that? Shouldn't we close connections before closing the server? – João Pimentel Ferreira May 15 '23 at 10:08
  • close() stops accepting new connections but will keep all existing connections. As long as connections exists it will not close. I think, when you call closeAllConnections() before close() there is a possibility that a connection is created between closeAll..() and close(). Then close() will not close until this connection closes itself. – Welcor May 15 '23 at 19:18
  • @Welcor I don't think so, if you call `close` without previously calling `closeAllConnections`, if you have a pending connection (for example waiting for a response), `server` will never trigger its callback. That's why before node 18 you have to force exit after a timeout. – João Pimentel Ferreira May 15 '23 at 20:01
  • the pending connection will be closed by closeAllConnections() after the close(). this then triggers the close() callback. timeline: close() -> closeAllConnections() -> close() callback called. – Welcor May 15 '23 at 20:36
0

Most answers call process.exit(), I don't think this is a good idea. You probably need to perform some teardown, also it's simply not needed.

const server = app.listen(port);

server.on('close', () => {
  // Perform some teardown, for example with Knex.js: knex.destroy()
});

// FYI Docker "stop" sends SIGTERM
// If SIGTERM is not catched, Docker is forced to kill the container after ~10s
// and this provokes an exit code 137 instead of 0 (success)
process.on('SIGTERM', () => server.close());

Check Express.js 4.x documentation: https://expressjs.com/en/advanced/healthcheck-graceful-shutdown.html#graceful-shutdown

tanguy_k
  • 11,307
  • 6
  • 54
  • 58
  • I think you missed the 1st sentence in the question: "I need to close server after getting callback from /auth/github/callback url." – Vladimir Starkov Feb 14 '23 at 12:19