node.jsexpresskeystonejs

Can you call "express()" more than once in an ExpressJS application? (Or: what exactly is "express()" doing?)


I've been using Express for a while but suddenly I'm unsure about something pretty basic.

I'm trying to add custom middleware to a KeystoneJS application -- specifically I'm adding a JWT token endpoint to a TinyMCE custom field

The custom field is

export let Wysiwyg = {
  type: 'Wysiwyg',
  implementation: Text.implementation,
  views: {
    Controller: Text.views.Controller,
    Field: importView('./views/Field'),
    Filter: Text.views.Filter,
  },
  adapters: Text.adapters,
  prepareMiddleware,
};

and prepareMiddleware is

function prepareMiddleware() {
  const tinymcePath = dirname(require.resolve('tinymce'));
  const app = express();
  app.use(cors());

  app.use('/tinymce-assets', express.static(tinymcePath));
  app.post('/jwt', function (req, res) {
    // NOTE: Before you proceed with the TOKEN, verify your users' session or access.
    const payload = {
      sub: '123', // Unique user id string
      name: 'John Doe', // Full name of user

      // Optional custom user root path
      // 'https://claims.tiny.cloud/drive/root': '/johndoe',

      exp: Math.floor(Date.now() / 1000) + (60 * 60) // 60 minutes expiration
    };

    try {
      const token = jwt.sign(payload, privateKey, { algorithm: 'RS256'});
      res.set('content-type', 'application/json');
      res.status(200);
      res.send(JSON.stringify({
        token: token
      }));
    } catch (e) {
      res.status(500);
      res.send(e.message);
    }
  });
  return app;
}

This is all called from a KeystoneJS app that has its own ExpressJS server running. What exactly is the call to express() above doing? The ExpressJS API docs say

**express()**

Creates an Express application. The express() function is a top-level function exported by the express module.

var express = require('express')
var app = express()

I always understood this to be creating a new HTTP server. Surely you don't want to do that twice in a single app unless you're serving on different ports (which I'm not trying to do)?

Similarly, the KeystoneJS docs say

If you need to provide your own custom middleware for your system you can create a custom App and include it in your exported apps.

class CustomApp {
  prepareMiddleware({ keystone, dev, distDir }) {
    const middleware = express();
    // ...
    return middleware;
  }
}

Here, again, they're calling express().

What exactly happens when you callexpress()? It starts a new Express app, according to the docs, but I always thought this was equivalent to starting a new server. Surely, I thought, you can't start two servers on the same port?

I am seeking to clear up some confusion -- I'm obviously not seeing the forest for the trees.


Solution

  • express() basically just creates a stack of middleware functions. It's not a server on its own.

    Because it's just a stack of middleware, an Express app can be 'mounted' into another app. An example is shown here (edited for brevity):

    var sub2 = express();
    sub2.get("/", (req, res) => { res.json({}); });
    
    var app = express();
    app.use("/foo", sub2);
    

    Defining and use()ing a new Express instance is really no different from loading any other middleware stack, such as express.Router().

    As for binding to ports, usually, you'll only call the listen() helper function on the upper-most Express app instance. All this does is set up a basic HTTP server (so you don't have to) and registers your Express instance as the callback. It's little different from doing http.createServer(options, myUpperMostExpressApp);.