• Stars
    star
    1,010
  • Rank 45,519 (Top 0.9 %)
  • Language
    JavaScript
  • Created over 5 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An example Electron app with a backend server all wired up via IPC

electron-with-server-example

See this post for background. This project demonstrates how to use a background server in an Electron app. It creates a background process, an IPC mechanism to communicate with it, and does it all in the safest way possible.

This is exactly how my product Actual, a personal finance manager, works and this code is almost 100% copy and pasted from it.

You have to know a decent amount about Electron to set up a background process which is why I created this.

Note: running a node process is dangerous. If the node process gets compromised in any way and you haven't sandboxed your app (available on macOS and Windows), the attacker has full access to the OS. You need to avoid running untrusted JS at all costs if you are going to enable node anywhere in your app. Be careful. This is meant to be used with 100% local apps where even the frontend is pulled from local files, not from a webserver.

Running

$ yarn install
$ yarn start

All yarn start does is run electron . which starts your electron app for development. Note how the background server runs in a window in development for debugging, but if you packaged it up with something like electron-builder it would run as a normal node process in the background.

How it works

Electron has two types of processes: a renderer process which represents a page with UI and a main process which handles all the renderers. It provides an IPC (inter-process communication) channel for sending messages between a main and renderer process, but you really want to be careful using it. Electron's IPC is used for anything critical like interacting with menus, coordinating renders, and more. If you block it, you will quickly see performance degradation (I think recent version have mitigated this possibility somewhat).

A naive implementation of a backend process would be something like this:

renderer (client) <----> main <----> renderer (backend)

Create a second renderer process and communicate with the client by sending messages through the main process. But you really don't want to be clogging Electron's IPC, and it seems wasteful to go through the main process when you just want to talk to the client.

See an article in 2018 about blocking the main process, and this github issue. Supposedly in version 5 the main process doesn't block the renderer as much, but you still want to avoid it as much as possible.

Lastly, it's weird to create a renderer process when all you want is to run some node code. Sure, you can create a hidden window but surely there's overhead with that? Luckily, there's a way to create a normal node process!

Creating a node process from Electron

If you fork electron's process it creates a node process. That's all you have to do. (see this issue). The following code runs server.js as a normal node process:

let { fork } = require('child_process')
let serverProcess = fork(__dirname + '/server.js')

Great! So now we have the backend as node process:

renderer (client) <----> main <----> node (backend)

You can communicate to it like you would a normal subprocess in node. However, we'd still like to avoid the dependency on the main process...

node-ipc

The solution is creating your own IPC mechanism. I used node-ipc which works well. That will create a local socket that the client and server uses to talk directly with each other.

So even though the main process is controlling 2 renderer processes, the communication now happens directly:

renderer (client) <----> main <----> node (backend)
    ^                                   ^
    |_________(your socket)_____________|

node-ipc will create a unix domain socket (or the equivalent on Windows) which is more performant than a WebSocket because it doesn't have network and messaging overhead.

The IPC implementation can be seen in client-ipc.js and server-ipc.js.

It finds an available socket but trying to connect and making sure nothing is currently running (this way multiple instances of the app can run at once). Then it passes the socket name to the server process as an argument, and passes it to the client by posting a message once the window is ready.

The server starts up an IPC server, and the client connects to it. Now they can directly communicate with each other.

Exposing node-ipc to the client

The client window is created with nodeIntegration disabled. This means it's a normal web page, but how can it access the node-ipc library then?

The answer is passing the preload option to run client-preload.js as the preload file. Electron will run this special file with node enabled when renderer process is created, allowing you to expose specific thing to the window. We expose an ipcConnect method to access the IPC and a few other things.

This can be dangerous. If you are loading untrusted JS in the page, don't do this. You are exposing objects onto the page that have access to full node capabilities, and attackers can potentially abuse JavaScript to inject malicious code that will run in this scope. This goes for anything created from a preload file, not just this technique.

One mitigation is the contextIsolation option of the BrowserWindow settings. It will run the preload file in a separate JS context, but unfortunately I can't figure out how to expose anything to the renderer's window with it on. If you want to expose stuff, you might be stuck with a dangerous preload, so you can't do this in apps that require untrusted JS.

Server methods

Once the backend process is created and the IPC connection is established, the client can invoke server methods via the send method:

let result = await send('make-factorial', { num: 2 })

The backend implements all its methods in server-handlers.js. This file exports a simple object that you can add any methods to, like this:

handlers['make-factorial'] = async ({ num }) => {
  console.log('making factorial')
  return num * 2
}

The internal mechanism for how messages are handled on the server/client can be seen inclient-ipc.js and server-ipc.js.

Debug mode

The last trick is enabling faster development. Right now Electron owns the node process, so one technique would be to simply run the server in your own node process like node server.js and then edit the Electron code to only create the client. This is certainly a valid option!

You can then restart the server as much as you like, and pass any debug node flags to the process.

However, managing multiple processes is annoying. It's also annoying if you want to debug the node process to create the devtools inspector and handle all that.

We have the devtools in Electron, why don't we just use that? In dev mode, this project loads the server in a background window instead of a background process! It's easy to do since we just create a renderer process with node integration enabled, so all of node is still there, but we also get a window!

In production mode, it creates a normal node process in the background.

Now you can use the fancy console, debugger, and stellar performance tools on your backend easily. You can even restart the server by simply reloading the window - and the renderer process doesn't even have to restart.

See this post for more examples of the cool stuff you can do with this.

More Repositories

1

absurd-sql

sqlite3 in ur indexeddb (hopefully a better backend soon)
JavaScript
4,172
star
2

transducers.js

A small library for generalized transformation of data (inspired by Clojure's transducers)
JavaScript
1,722
star
3

blog

All the sources for my (not powered by React anymore) blog
CSS
1,223
star
4

crdt-example-app

A full implementation of CRDTs using hybrid logical clocks and a demo app that uses it
JavaScript
616
star
5

css-animations.js

A library to work with CSS3 keyframe animations from javascript
JavaScript
479
star
6

backend-with-webpack

A simple server using webpack as a build tool
JavaScript
458
star
7

unwinder

An implementation of continuations in JavaScript
JavaScript
304
star
8

canvas-game-bootstrap

A starting point & tutorial for basic 2d canvas games
JavaScript
237
star
9

es6-macros

A collection of sweet.js macros that implement ES6 features for ES5
JavaScript
237
star
10

monkey-hot-loader

A webpack loader to hot reload JavaScript modules
JavaScript
171
star
11

outlet

Outlet is a simple Lisp-like programming language
JavaScript
161
star
12

jsx-reader

A JSX reader for JavaScript, powered by sweet.js.
JavaScript
148
star
13

lively

Components & state
JavaScript
145
star
14

emojiscript

EmojiScript: emotion literals, emotional algebra, and more for JavaScript
JavaScript
139
star
15

dom3d

Rendering 3d with CSS3
JavaScript
111
star
16

gambit-iphone-example

An example iphone app which uses Gambit Scheme.
Scheme
103
star
17

farmageddon

The raw, unedited source for the iOS game I wrote in Gambit Scheme back in 2010.
C
102
star
18

dcpu-lisp

A Lisp-like language that compiles to DCPU-16 assembly code
JavaScript
77
star
19

redux-experiments

A few customizations for my own projects
JavaScript
64
star
20

lljs-cloth

Cloth simulation as a large-scale test for LLJS development
JavaScript
47
star
21

sweetjs-loader

A webpack loader for sweetjs
JavaScript
47
star
22

swank-gambit

Gambit swank backend for SLIME
Scheme
40
star
23

absurd-example-project

A project to show everything needed to use absurd-sql
JavaScript
40
star
24

learning-clojurescript

All the apps I'm making to learn ClojureScript (and also have fun)
JavaScript
40
star
25

grunt-nunjucks

A grunt plugin for nunjucks.
JavaScript
38
star
26

steps

A little hack that allows you to step through JavaScript code (only in Firefox for now)
JavaScript
30
star
27

jlongster-rebuild

A rebuild of my blog w/react, CSP, and more
JavaScript
29
star
28

gulp-sweetjs

A gulp loader for sweet.js
JavaScript
26
star
29

play-react-native-opengl

An example of using React Native to control OpenGL (terrible hacky code)
JavaScript
26
star
30

sweet.js-tutorials

All the sweet.js tutorials from my blog and a working sweet.js environment
JavaScript
26
star
31

webfighter

An r-type inspired 2d game for the web
JavaScript
24
star
32

live-css-layout

A live editor for css-layout
JavaScript
20
star
33

configure-iphone

A utility shell script to help build libraries for iPhone devices.
19
star
34

objective-c-csv-parser

Objective-C CSV Parser
18
star
35

shade

A simple but optimal 3d engine
JavaScript
16
star
36

mozlando-react-workshop

Lessons for learning React.
JavaScript
15
star
37

construct.js

Simple sugar for establishing prototype-based inheritance
JavaScript
15
star
38

genetic-canvas

Implements a genetic algorithm for drawing 2d pictures
Scheme
14
star
39

tcomb-immutable-experiment

An experiment with immutable.js-backed tcomb types
JavaScript
14
star
40

outlet-machine

A register-based virtual machine for a basic Lisp
JavaScript
13
star
41

autoffi

FFI generator for Gambit Scheme
Scheme
13
star
42

ftgles

FTGL, a font rendering library, ported to OpenGLES.
C++
12
star
43

struct.js

Struct datatypes for JavaScript with a few sweet.js macros
JavaScript
12
star
44

gambit-ffi-generator

Generating Gambit-C Scheme FFI's from C header files
12
star
45

fp-workshop

A workshop to introduce functional-style programming in JavaScript
11
star
46

schemeray

A simple raytacer written in Gambit Scheme, and ported to other implementations
Scheme
11
star
47

mozilla.com

Battlegrounds for mozilla.com work (mirror of http://svn.mozilla.org/projects/mozilla.com/)
PHP
10
star
48

devrepl

A REPL for the Firefox debugger client/server
JavaScript
10
star
49

reason-http-server-example

A simple HTTP server in Reason using cohttp and built with Rebel.
10
star
50

weatherme

A simple weather web app
JavaScript
8
star
51

macro-editor

An embeddable sweet.js macro editor
JavaScript
8
star
52

flame

An experimental tile-based game
JavaScript
7
star
53

mozilla.org

A copy of the mozilla.org PHP site.
JavaScript
7
star
54

grime3d

A proof of concept for something special.
Scheme
6
star
55

OMGBUGS

Experimental nodejs-powered bugzilla interface. See the demo site at this repo's url.
JavaScript
6
star
56

perf-deets

Get those perf deets
JavaScript
6
star
57

game-engine-studies

Implementing various game engines in javascript
JavaScript
6
star
58

lljs-minecraft

Port of notch's minecraft demo to LLJS
JavaScript
5
star
59

ahoy

This is nothing.
JavaScript
5
star
60

tilt-this

A web-based builder for 3d Tilt objects (viewable by Firefox's Tilt devtool)
JavaScript
5
star
61

actual-automoto

JavaScript
5
star
62

ftgles-gambit

Gambit Scheme bindings to the FTGLES library
Scheme
4
star
63

webui

Using csuwldcat's WebComponent project to build some UI tools for webapps
JavaScript
4
star
64

relnotes

Release notes generator for Firefox releases
Python
4
star
65

lljs-editor

An experiment with live-editing LLJS code in the browser.
JavaScript
4
star
66

ff-devtools-libs

A quick experiment to help migrate to HTML
JavaScript
4
star
67

demo-cloth

A floating cloth demo with JavaScript/WebGL
JavaScript
4
star
68

channel-debugger

a firefox devtool addon for debugging channels
JavaScript
4
star
69

YPS

Yield Passing Style
JavaScript
4
star
70

entity.js

A little game experiment
JavaScript
3
star
71

spidermonkey-graphics

spidermonkey for 3d graphics and user interfaces
C
3
star
72

writing-webapps

My presentation at the Mozillas Apps Summit
JavaScript
3
star
73

strangeloop2015

Slides for my talk "Cleaning the Tar: Using React in Firefox DevTools"
JavaScript
3
star
74

tweetness

A twitter Open Web App
JavaScript
3
star
75

js-lljs-c-benchmarks

Benchmarks comparing js, lljs, and c compiled with emscripten
JavaScript
3
star
76

octave-utility

my utility code for using octave
Objective-C
3
star
77

jlongster-django

My personal site: jlongster.com
Python
3
star
78

django-basic-site

Python
3
star
79

fiber-modal-error

JavaScript
2
star
80

gollumdoc

Patches the gollum wiki to host documentation better
Ruby
2
star
81

persona.org

NodeJS project
JavaScript
2
star
82

mozilla-product-details

git svn clone http://svn.mozilla.org/libs/product-details/json/
JavaScript
2
star
83

voloweb

A site for volo
JavaScript
2
star
84

servable-branches

Manifest git branches on the filesystem to be served on the web
JavaScript
2
star
85

build-docker-mochitest

Making mochitests OK.
Makefile
2
star
86

xpcshell-unit-test

A basic xpcshell test runner that can be used outside of Firefox
JavaScript
2
star
87

libgrowth

A tiny web site that estimates the growth of your node libraries over time
JavaScript
2
star
88

my-react-native-babel-preset

JavaScript
2
star
89

onGameStart-2013

My presentation for onGameStart 2013, using websockets to connect everybody
JavaScript
2
star
90

devtool-prototype

An experiment with making a new devtool
CSS
2
star
91

actual-docs

markdown content
JavaScript
2
star
92

rn-android-input-pan

Repro for Android's panning behavior when focusing an input
Objective-C
2
star
93

jlongster.com

JavaScript
2
star
94

bugzillersearch

A quick experiment in hating iOS for nefarious research purposes.
Objective-C
2
star
95

cordova-firefoxos-plugins

A place for Mozilla to track our Cordova work
Java
2
star
96

php-product-details

Mozilla's PHP product-details library (mirror of https://svn.mozilla.org/libs/product-details)
PHP
2
star
97

services

services that I run on my computer & servers
Ruby
1
star
98

tmp-node-inspector

JavaScript
1
star
99

jlongster.github.io

HTML
1
star
100

noodleconfware

The conference software for noodleconf
JavaScript
1
star