Using Express inside Electron

by Samuel Attard

I'm going to be honest, this was a bait and switch title. Mainly because in my opinion you should never use Express inside Electron. There are a number of reasons for this, so in no particular order.


  • You would be opening up a publicly accessible port on your users machine. Any program on that machine or any website loaded in a browser could connect to your local express instance.
  • It won't work on some user's machines due to their firewall settings. It simply won't let you open the port.
  • Express doesn't make much sense in the world of Electron. Why host an entire server to send content between two processes running on the same machine. That's what IPC is for...

So why do people want to use it then?????

Well that's an easy question to answer, they like it, the api is simple and it means they can make REST based web apps inside Electron with an architecture they are used to.

So what's the alternative

So I got bored one day and made a tool called electron-router. Basically it wraps some electron API's (primarily registerStringProtocol and registerStandardSchemes) and presents you with an API incredibly similar to how Express operates.

Here is a super simple example of what you can do with electron-router.

// In the main process
import { Router } from '@marshallofsound/electron-router'

const api = new Router('myapp');

let me = { name: 'Samuel' };  
api.get('me', (req, res) => {  
  res.json(me);
});
api.post('me', (req, res) => {  
  me = req.uploadData[0].json();
  res.json({ status: 'success' });
});

Now from the renderer process we can use our router API like any other HTTP endpoint, just with our custom scheme out the front. For example:

// In the renderer process
import { rendererPreload } from '@marshallofsound/electron-router';

rendererPreload(); // This has to be called once per renderer process

fetch('myapp://me')  
  .then(resp => resp.json())
  .then(me => console.log(me.name)); // Samuel

fetch('myapp://me', {  
  method: 'POST',
  body: JSON.stringify({
    name: 'Jimmy'
  })
}).then(() => {
  fetch('myapp://me')
    .then(resp => resp.json())
    .then(me => console.log(me.name)); // Jimmy
});

Just like express you can do all the standard HTTP methods. router.get, router.post, router.put, router.delete and of course the express router.use. This use method works in exactly the same way to Express. They are always called first so you can use .use to pass information through to later .[method] listeners.

Just like express you can also call .use with extra routers. To do that with electron-router you simply import "MiniRouter" and use it just the top level router. E.g.

import { Router, MiniRouter } from '@marshallofsound/electron-router';

const api = new Router();  
const subAPI = new MiniRouter();  
api.use('sub/thing', subAPI);  
subAPI.get('magic', (req, res) => res.send('Hello'));  

I've personally started using this as a semi-replacement for the renderer -> main -> renderer IPC communication pathway that most people currently use. I've found it a lot closer to what people coming from web technology backgrounds are used to and it has greatly improved both the speed at which I develop and the code readability.

Let me know what you think of this module in the comments below, totally not biased or anything. But I think it's pretty awesome :D