Electron Adventures: Episode 18: Sending Data To Backend
Tags: javascript, electron
Published: 2021-08-11 21:46:55

Electron apps have frontend process (called "renderer") and backend process (called "main"). There's also small bridging code in between ("preload") that has access to backend API on the frontend.

So far we completely ignored the backend process, and did everything in the frontend and the preload.

Let's see how backend and frontend can communicate. We'll turn off all security for now, so we can see the relevat parts more clearly. We'll get to how to do it more securily later.

### Starting a new app

Let's do something about that. Starting a new no-framework:

```shell
$ npm init -y
$ npm install --save-dev electron
```

### `index.html`

Let's start with a simple `index.html`. Other than some styling, it's just a single line form, plus some div for printing data.

```html
<!DOCTYPE html>
<html>
  <body>
    <style>
      body {
        background-color: #444;
        color: #ccc;
        font-family: monospace;
        font-size: 24px;
      }
      form {
        display: flex;
      }
      input {
        background-color: inherit;
        color: inherit;
        font-family: inherit;
        border: none;
        font-size: inherit;
        flex: 1;
      }
    </style>
    <h1>Print to terminal</h1>
    <form>
      <input type="text" autofocus />
    </form>
    <div id="responses"></div>
    <script src="app.js"></script>
  </body>
</html>
```

### backend code `index.js`

We can start it just like before. We'll add one extra thing to this file, later, but for now, let's just open `index.html` and give it full privileges:

```javascript
let { app, BrowserWindow } = require("electron")

function createWindow() {
  let win = new BrowserWindow({
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    }
  })
  win.loadFile("index.html")
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
```

### frontend code `app.js`

In frontend we need an event handler for when user submits data. So we grab a few DOM elements, and then get data submitted, send it to backend, get its response, and append it to the `#responses` div.

```javascript
let form = document.querySelector("form")
let input = document.querySelector("input")
let responses = document.querySelector("#responses")

form.addEventListener("submit", async (e) => {
  e.preventDefault()
  let line = input.value
  input.value = ""
  let responseText = // what do we do here?
  let response = document.createElement("div")
  response.textContent = responseText
  responses.appendChild(response)
})
```

How can we send data to the backend? Here's how:

```javascript
let { ipcRenderer } = require("electron")

let form = document.querySelector("form")
let input = document.querySelector("input")
let responses = document.querySelector("#responses")

form.addEventListener("submit", async (e) => {
  e.preventDefault()
  let line = input.value
  input.value = ""
  let responseText = await ipcRenderer.invoke("console", line)
  let response = document.createElement("div")
  response.textContent = responseText
  responses.appendChild(response)
})
```

IPC is "inter-process communication", or a way for different processes to communicate. It sort of looks like we're calling a function (which I called `console` - but that's completely arbitrary) in the main process.

Behind the scenes arguments get serialized (sort of like turned into JSON string), promise returned, and then once we get response, the response gets deserialized (sort of like turnes from JSON string back into normal objects), and promise resolves to whatever backend returned.

### Backend handler

Backend has `ipcMain` corresponding to `ipcRenderer`. `invoke` corresponds to `handle`.

```javascript
let { ipcMain } = require("electron")

ipcMain.handle("console", (event, line) => {
  console.log(`Received from frontend: ${line}`)
  return `Backend confirms it received: ${line}`
})
```

As you can guess, it works similar both ways, if you wanted to send the messages from backendh to frondent you'd do `ipcMain.invoke` and `ipcRenderer.handle`.

There are also a few other ways to communicate than just `invoke`+`handle`, and we'll get some uses for them eventually.

### Result

And here's the result:

![Episode 18 Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r44q8k5r4zqz06iz2v28.png)

As usual, [all the code for the episode is here](https://github.com/taw/electron-adventures/tree/master/episode-18-sending-data-to-backend).