135

I'm trying to figure out how to shut down an instance of Express. Basically, I want the inverse of the .listen(port) call - how do I get an Express server to STOP listening, release the port, and shutdown cleanly?

I know this seems like it might be a strange query, so here's the context; maybe there's another way to approach this and I'm thinking about it the wrong way. I'm trying to setup a testing framework for my socket.io/nodejs app. It's a single-page app, so in my testing scripts (I'm using Mocha, but that doesn't really matter) I want to be able to start up the server, run tests against it, and then shut the server down. I can get around this by assuming that either the server is turned on before the test starts or by having one of the tests start the server and having every subsequent test assume it's up, but that's really messy. I would much prefer to have each test file start a server instance with the appropriate settings and then shut that instance down when the tests are over. That means there's no weird dependencies to running the test and everything is clean. It also means I can do startup/shutdown testing.

So, any advice about how to do this? I've thought about manually triggering exceptions to bring it down, but that seems messy. I've dug through Express docs and source, but can't seem to find any method that will shut down the server. There might also be something in socket.io for this, but since the socket server is just attached to the Express server, I think this needs to happen at the express layer.

João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
drewww
  • 2,485
  • 4
  • 22
  • 24

6 Answers6

203

Things have changed in Express 3 because the express server no longer inherits from the node http server. Fortunately, app.listen returns the server instance.

var server = app.listen(3000);

// listen for an event
var handler = function() {
  server.close();
};
Kevin M. Mansour
  • 2,915
  • 6
  • 18
  • 35
Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129
  • 28
    In the case of Mocha testing, where you require('app') I extend onto the app object: app.server = app.listen(3000); so later I can say: var app = require('./app'); app.server.close(); – Jack Chi Feb 06 '15 at 23:36
  • 2
    when testing server, check out https://github.com/visionmedia/supertest it will let you test w/o launching actual server – Lukas Liesis Nov 01 '16 at 13:54
  • I would also suggest you pass the done callback to `server.close()` if calling this from within a hook. – Cabrera Aug 30 '18 at 16:47
  • 3
    Note: There is a big difference between 'app', which is the Express Application instance, and the return value of 'app.listen', which is the underlying native HTTP Server instance which has the 'close' method. @JackChi hinted at this above. – ajxs Sep 17 '18 at 05:20
  • Doesn't this cause problems with open client sockets that are left open? – Cameron Tacklind Feb 10 '20 at 20:48
  • Do you know if `server.close()` allow a callback? Something like `server.close(()=>{console.log('server closed')});`? – João Pimentel Ferreira Jul 15 '21 at 12:45
  • 1
    @JoãoPimentelFerreira Yes, it's defined: `Server.close(callback?: ((err?: (Error | undefined)) => void) | undefined): Server` In simple JS terms, that means it takes a callback and passes an optional arg if there is an error – Ron S. Jul 15 '21 at 23:49
  • @LukasLiesis Do you know how to stop the server which is runned by the Supertest? I have a test when I set an incorrect content type and fail into try/catch and get a warning: `Jest did not exit one second after the test run has completed.` – MegaCasper Aug 27 '22 at 12:03
19

Use app.close(). Full example:

var app = require('express').createServer();
app.get('/', function(req, res){
  res.send('hello world');
});
app.get('/quit', function(req,res) {
  res.send('closing..');
  app.close();
});
app.listen(3000);

Call app.close() inside the callback when tests have ended. But remember that the process is still running(though it is not listening anymore).

If after this, you need to end the process, then call process.exit(0).

Links:

app.close: http://nodejs.org/docs/latest/api/http.html#server.close (same applies for)

process.exit: http://nodejs.org/docs/latest/api/process.html#process.exit

  • 1
    Perfect, exactly what I was looking for. I guess I didn't find it in Express because they were extending the core node http server, which I didn't totally understand. Thanks! – drewww Dec 28 '11 at 19:59
  • Thanks Srijan, that's helped me too – Adam Hopkinson Mar 16 '12 at 09:45
  • Is this still true? express.createServer is marks as deprecated, and gives an error saying app no longer inherits from http.js's server – Frank Schwieterman May 28 '13 at 21:32
  • For a method that works better with recent express releases, see my answer. – Rich Apodaca Jun 07 '13 at 17:03
  • 10
    This is not valid since express 3. – gprasant Dec 10 '13 at 05:14
  • app.close does not work anymore, but process.exit(0) does the job – Alex Albu Sep 03 '16 at 08:18
  • 8
    Exposing a URL for closing a server like this is not a good idea IMHO. – shime Sep 02 '17 at 16:29
  • Agree w/ @shime, exposing a route to shutdown your app can have bad consequences. Someone running a wordlist against your server to discover paths could crash your application unexpectedly. Confirmed the selected answer works w/ Express 4. – jaywon Sep 09 '19 at 07:44
  • Do you know if `server.close()` allow a callback? Something like `server.close(()=>{console.log('server closed')});`? – João Pimentel Ferreira Jul 15 '21 at 12:46
  • Using `process.exit()` in node is almost certainly wrong. Node automatically exits when there are no events left in the event loop. The right way is to shut down the server, and let Node automatically terminate. If Node isn't terminating, you still have events that need to get their own termination signal (maybe timers are running)? `process.exit()` is *never* a clean shutdown. – ErikE Mar 06 '23 at 05:15
4
//... some stuff 

var server = app.listen(3000);
server.close();
аlex
  • 5,426
  • 1
  • 29
  • 38
  • Do you know if `server.close()` allow a callback? Something like `server.close(()=>{console.log('server closed')});`? – João Pimentel Ferreira Jul 15 '21 at 12:46
  • 1
    @JoãoPimentelFerreira yes, you can. Have a look to the http package in official Nodejs docs. https://nodejs.org/api/http.html#http_server_close_callback – Siro_Diaz Oct 05 '21 at 00:42
3

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 HTTP 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

Usage with Express.js:

import express from 'express';
import {
  createHttpTerminator,
} from 'http-terminator';

const app = express();

const server = app.listen();

const httpTerminator = createHttpTerminator({
  server,
});

await httpTerminator.terminate();

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • you forgot to mention the most important part which is where to call the terminate method? inside the SIGINT handler? SIGTERM handler or something else? – PirateApp Dec 05 '22 at 12:37
1

More recent version of express support this solution:

const server = app.listen(port);

const shutdown = () => {
  server.close();
}
Filip Górny
  • 1,749
  • 16
  • 24
-2

You can easily do this by writing a bash script to start the server, run the tests, and stop the server. This has the advantage of allowing you to alias to that script to run all your tests quickly and easily.

I use such scripts for my entire continuous deployment process. You should look at Jon Rohan's Dead Simple Git Workflow for some insight on this.

Josh Smith
  • 14,674
  • 18
  • 72
  • 118
  • 3
    This would work, but I'd prefer a bash-free solution if possible. Maybe it's a stupid preference, but I'd like to be starting/stopping the server from the test context because it makes it easier to write server-config-specific tests + startup/shutdown tests. Plus, it doesn't introduce the indirection of having to run server-related tests through a separate script. – drewww Dec 28 '11 at 17:59
  • Why are you writing environment-specific tests? Maybe I'm wrong, but this strikes me as bad practice. I would try to stay environment agnostic with your testing, as you would want your tests to work across your team regardless of their development environment. – Josh Smith Dec 28 '11 at 18:06
  • I'm not doing anything environment-specific here, I don't think. There will be a handful of different startup options for the server (like reading config options from a file, loading state from a datastore, etc) that it would be nice to test in the same framework I test everything else. I'd also like to have tests that, for instance, shutdown the server, bring it back up again, and make sure it didn't lose state in the process. That's easier if I can do it programmatically from node than having testing code in bash, too. – drewww Dec 28 '11 at 18:14
  • You wouldn't place your test code in bash itself. You'd simply start up the server and run the tests from the script, just as you would do yourself on the command line. There's no real special magic there. – Josh Smith Dec 28 '11 at 18:37