To hook Express into a plain http server that is already created, you just need to register the app variable for the request event like this:
server.on('request', app);
This is exactly what passing a callback to http.createServer() does for you.
And, here's a working app that creates the http server separately:
const http = require('http');
const app = require('express')();
const server = http.createServer((req, res) => {
if (req.url === '/foo') {
console.log("plain handler", req.url);
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end("foo");
}
});
app.get("/demo", (req, res) => {
console.log("expess handler", req.url);
res.send("demo");
});
app.use((err, req, res, next) => {
// only handle error if someone hasn't already sent a response
if (!res.headersSent) {
res.status(404).send("Error 404");
}
});
// register Express app as a handler for the request event
server.on('request', app);
server.listen(80);
Note: When using two separate systems like this, there are some complications because both event handlers for the request event (the one passed to createServer() and the one registered with server.on('request', ...) will get called on each http request, even if one has already handled it. This is because the two systems aren't built to work together that way. You essentially have two separate event listeners trying to process each request. You may occasionally have to check res.headersSent to see if the other has already processed the request or not.
If you want to look inside the workings of node.js, you can see that http.createServer() is just this code:
function createServer(opts, requestListener) {
return new Server(opts, requestListener);
}
And, the Server object is a subclass of Net.server and the constructor contains this code:
if (requestListener) {
this.on('request', requestListener);
}