Elm in the server (or anywhere else) with promises
Tags: elm, javascript
Published: 2021-01-04 14:20:44

Elm was created to run on the browser, but every once in a while someone will ask about how to run Elm in a server environment.

At my current job, we needed to sync several clients and persist that shared state somewhere, so we thought it would be a good idea if the server could act like another client that could persist that state in a centralized place.

For that, we created a Node/Express server to run our Elm code and at first, it was very hackish.

In a server-like environment, you mostly always have a request and a response tied together. You ask for something and you might get what you requested or an error. It doesn't matter, for every request, there is a response.

But Elm doesn't work like that if you wanna talk to the outside world. Yes, you can use [ports](https://guide.elm-lang.org/interop/ports.html) for outside communication, but ports follow the actor model of message passing. So contrary to the request/response nature of server communication, you can only send and receive messages in Elm. That might sound like the same thing but it is not. You can receive a message without ever sending one in the first place. Or send a message without the need to wait for a message back. You can send a message and receive multiple messages back and so on. There is no coupling between sending and receiving a message and that makes Elm kinda unsuitable for a server software where request/response messages are tied.

After looking for better solutions I came across [this post](https://discourse.elm-lang.org/t/imitating-synchronicity-with-ports/1930) in the forums where the user [joakin](https://discourse.elm-lang.org/u/joakin) made a clever suggestion: just send the **response** object from the JavaScript side to a port and send it back through another port when replying to whatever it was requesting. Use the response object to send a proper response to the right client and there you go. You can see an example of that [on this helpful repository](https://github.com/joakin/node-elm-server).

That is something I didn't know: you can pass any JavaScript value as a [`Json.Decode.Value`](https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#Value) to Elm, even functions. Of course, you can't do much with them inside Elm but in this case, it helps to tie a specific function call to the message we will send back.

The idea is great and helps us to have some type of tied request/response flow. The problem is when we needed to test the integration. It was easier to bypass all the server stuff and focus on the interoperation between Node and Elm directly. Or even worse, what if the software we were writing wasn't a Node/Express server at all? That is when my boss and co-worker [Nate](https://twitter.com/nateabele) suggested we used promises. Instead of sending the response object from Express to Elm, we could send the resolve function from a promise!

I have made a fork from the example code above with these changes. You can check it out [here](https://github.com/eberfreitas/node-elm-server).

On the [Elm side](https://github.com/eberfreitas/node-elm-server/blob/master/src/Server.elm), nothing much has changed. I just made a few naming changes to better reflect the new nature of the interoperation with the JavaScript code. But other than that, we didn't have to change much to make this approach work as both the previous response object that was being sent from Express and the new resolve function from the promise are both just `Json.Decode.Value`s.

The real magic is on the [JavaScript code](https://github.com/eberfreitas/node-elm-server/blob/master/index.js). It is a little bit more complex but it decouples the Elm code and ports from Express itself, making it possible to use that approach virtually anywhere. Here is the bit that makes everything work:

```js
http
  .createServer((request, res) => {
    new Promise(resolve => app.ports.onRequest.send({ request, resolve }))
      .then(({ status, response }) => {
        res.statusCode = status;
        res.end(response);
      });
  })
  .listen(3000);

app.ports.resolve.subscribe(([{ resolve }, status, response]) => {
  resolve({ status, response });
});
```

So, it is possible to use Elm in the server, and I would argue that with that approach if you need some kind of tied request/response integration, you can use Elm anywhere where you can use Node. **But is it useful?** In our case, where we wanted to use most of the code from our client on the server it was a total win, but I would think twice if I wanted to build a full server with Elm as it just doesn't have all the things you will need to make it a good developing experience, although it would be possible.

Maybe [Roc](https://youtu.be/ZnYa99QoznE?t=4755) will be the language we will use for cases like that. Can't wait for it!

So, what do you think about this approach? Have you done something similar or vastly different to solve the same problem?

Thanks for reading!