Create a Phoenix LiveView Like App in JS with AppRun
Tags: apprun, javascript, elixir, websockets
Published: 2020-03-21 16:23:23

---
title: Create a Phoenix LiveView Like App in JS with AppRun
published: true
description: A step by step instruction to create WebSockets based AppRun Applications
tags: #apprun #javascript #elixir #websockets
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/pvittiwomh5rxyngm72f.png
---

## Introduction

When I was creating the [AppRun Websockets Template](https://github.com/yysun/apprun-websockets), I thought I was the only crazy person to push and run the 1 + 1 calculation on the server-side until I saw [this video](https://www.youtube.com/watch?v=U_Pe8Ru06fM). 

{% youtube U_Pe8Ru06fM %}

> Phoenix LiveViewis a server-side solution using the [Elixir](https://elixir-lang.org) programming language.  

My [AppRun](https://apprun.js.org) WebSockets solution shares the same idea with [Phoenix LiveView](https://github.com/phoenixframework/phoenix_live_view). 

However, the AppRun solution is 100% JavaScript. For those JavaScript/TypeScript developers that are not ready to learn another programming language, the AppRun solution is for you.

This post is a step by step instruction of how to create a WebSockets based AppRun application.

## Create the Project

To get started, run the following commands in the terminal or command shell. 

```bash
npx degit apprunjs/apprun-websockets my-app
cd my-app
npm install
npm start
```

You will see a single page application using Bootstrap. 

## Project Structure

The project has a typical express JS project structure. 

![Project Structure](https://dev-to-uploads.s3.amazonaws.com/i/9jhhtgn8zcmy0e8hlbdp.png)
<figcaption>Figure 1. Project Structure<figcaption>

* _server_ directory has the server-side code
* _server/index.js_ is the webserver
* _src_ directory has the client-side code
* _public_ directory has the static assets 
* _public/index.html_ is the default web page of the app

The project also has a few npm scripts.

* _npm start:client_: compiles and watches the client-side app
* _npm start:server_: starts the webserver
* _npm start_: starts client and server
* _npm run build_: build the client-side app for production

## Understand the Architecture 

### Client Side

On the _Home_ page of the SPA, there are two counters. One runs locally in the browser. One runs on the server. 

```javascript
import { app, Component } from 'apprun';

export default class HomeComponent extends Component {
  state = 0;
  view = state => {
    const add = (state, num) => state + num;
    return <div>
      <h1>{state}</h1>
      <div>
        <button $onclick={[add, -1]}>-1</button>
        <button $onclick={[add, +1]}>+1</button>
        <div>Run the counter locally</div>
      </div>
      <hr />
      <div>
        <button $onclick={['//ws:', '@add', state, -1]}>-1</button>
        <button $onclick={['//ws:', '@add', state, +1]}>+1</button>
        <div>Run the counter on server through web sockets</div>
      </div>
    </div>
  };
  update = {
    '@add': (_, state) => state
  }
}
```

They all use the AppRun event-driven programming model to trigger the state update and render lifecycle. The local counter uses local event directive. The server counter uses a global event //ws:, which means the send it to sever. The event parameters are @add, 1 or @add -1. 

The _main.tsx_ has five lines of code to send the event to the server through the web socket. The event name is @add. The event parameter is 1 or -1. 

```javascript
const ws = new WebSocket(`ws://${location.host}`);
app.on('//ws:', (event, state, ...args) => {
  const msg = { event, state, args };
  ws.send(JSON.stringify(msg));
});
```

### Server Side

On the server-side, index.js creates an express server and listen to the web socket communication. When it receives messages, it publishes the messages using AppRun. 

```javascript
const apprun = require('apprun').app;
require('./add');
const express = require('express');
const { createServer } = require('http');
const app = express();
const server = createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', function(ws) {
  ws.on('message', function (data) {
    try {
      const json = JSON.parse(data);
      apprun.run(json.event, json);
      ws.send(JSON.stringify(json));
    } catch (e) {
      console.error(e);
    }
  });
});
```

> Yes. AppRun runs on the server too to provide server-side event-driven programming. 

The _add.js_ is a server-side module that handles the @add event. It does the calculation to update the _state_. Then, in _index.js_, it sends results back to the web socket as an event using the same event name @add. 

```javascript
const app = require('apprun').app;
app.on('@add', function (data) {
  data.state += data.args[0];
});
```

### Back to Client

The _main.tsx_ receives the event from the web socket. It just needs to publish as it is. The event is a global event that has the name @add. The home page then handles the @add event to get the state calculated on the server. 

```javascript
ws.onmessage = function (msg) {
  const {event, state} = JSON.parse(msg.data);
  app.run(event, state);
}
```

We can summarize the process in the diagram below. 

![Events](https://dev-to-uploads.s3.amazonaws.com/i/pvittiwomh5rxyngm72f.png)
<figcaption>Figure 2. Events Between Client and Server<figcaption>

### Next Steps

You can use the [AppRun—WebSockets template](https://github.com/apprunjs/apprun-websockets) as the start point. The template has all you need to create your Phoenix LiveView like applications. 

Or you can copy about 20 lines of JS code from the _main.tsx_ and _index.js_ into your existing application to start leveraging the power of WebSockets. E.g., [database-driven applications using WebSockets](https://dev.to/yysun/database-driven-applications-using-websockets-2b9o). 

## Live Demo

{% glitch apprun-websockets app%}

## Conclusion

Because we use JavaScript, our applications are full-featured SPA. We can have [Bootstrap, D3, ChartJS and, even JQuery plug-ins](https://apprunjs.github.io/apprun-bootstrap/). 

The AppRun WebSockets solution is so simple that it does not even worth being an npm package. Use the [AppRun—WebSockets template](https://github.com/apprunjs/apprun-websockets) or copy about 20 lines of code into your codebase, you will have Phoenix LiveView like applications. 

One last thing to mention is that Phoenix LiveView does the server-side rendering (SSR), which is also [trivial using AppRun](https://github.com/yysun/apprun-ssr). However, I favor the Progress Web App (PWA) over SSR because PWA provides offline, home screen icon, and many other features. There is also an [AppRun template for PWA](https://github.com/apprunjs/apprun-pwa-workbox).