3

I create an express app like this

const express = require('express')
const app = express();

app.use(express.static(path.join(__dirname, 'public')));

app.post('/close', async (_, res) => {
  res.status(200);
  res.end();
  app.close();
});

module.exports = app;

I instantiate it in another module

const myApp = require('./app.js');
myApp.listen(port, () => {
  console.log(`Started server on ${port}`);
});

I want the server to shut itself down when it receives a post request to /close. At the moment, I just get a app.close is not a function error.

I know I can close a server externally like this

const server = myApp.listen(port, () => {
  console.log(`Started server on ${port}`);
});
server.close();

but I want to close the server on a post request to /close, how can I do that?

O. Jones
  • 103,626
  • 17
  • 118
  • 172
theonlygusti
  • 11,032
  • 11
  • 64
  • 119

2 Answers2

2

To get access to your server object, try using

req.connection.server

from within your route handler.

.close() makes the server stop listening for new connections. Already-established connections are not affected. The server object emits a 'close' event when all open connections have disconnected.

process.exit() stops your whole nodejs app.

So, this code might do what you want.

app.post('/close', (req, res, next) => {
  res.status(200)
  res.end()
  const server = req.connection.server
  if (server.listening) {
    server.addEventListener('close', ev => {
      console.log('server closed. See ya later alligator.')
      process.exit(0)
    })
    console.log('closing server')
    server.close()
  }
});

If you need to get the server object from your app object (if getting it from your req object isn't good enough), you could put in a little middleware function like this.

app.use( function adornApp( req, res, next ) {
  req.app.locals.server = req.connection.server
  next()
} )

Then you'll be able to get your server from app.locals.server once your middleware is first invoked.

O. Jones
  • 103,626
  • 17
  • 118
  • 172
0

You could use the http-terminator package to close your server. The following should do the trick. The reason we use the http-terminator is because the server won't close if it is visited via a route.

const { createHttpTerminator } = require("http-terminator");
const { initApp, app } = require("./app.js");

const PORT = 3000;

const server = app.listen(PORT, () => {
  console.log(`Started server on ${PORT}`);
});

const httpTerminator = createHttpTerminator({ server });

initApp(httpTerminator);

Inside the module:

const express = require("express");
const app = express();

const initApp = (httpTerminator) => {
  app.get("/close", async (_, res) => {
    res.json({ message: "we closed" });
    await httpTerminator.terminate();
  });
};

module.exports = { initApp, app };
Reyno
  • 6,119
  • 18
  • 27
  • This still requires the httpServer instance, which my router doesn't have access to – theonlygusti Nov 16 '20 at 11:13
  • @theonlygusti you could add a wrapper function inside your module and export that as well. Then add all routes that use the `httpTerminator` instance inside the function. See code snippet – Reyno Nov 16 '20 at 11:22
  • 1
    @Reyno, great approach, but I think OP wants to close the server without having to refactor his code: access the server instance. – Adam Azad Nov 16 '20 at 11:24