- Using the Glean programming language:
Gleam
is a general-purpose, concurrent, functional high-level programming language that compiles to Erlang or JavaScript source code. Gleam is a statically-typed language, which is different from the most popular languages that run on Erlang’s virtual machine (BEAM
),Erlang
andElixir
. Gleam has its own type-safe implementation of OTP, Erlang's actor framework. Packages are provided using theHex
package manager, and an index for finding packages written for Gleam is available. Thanks to a multi-core actor based concurrency system that can run millions of concurrent tasks, fast immutable data structures, and a concurrent garbage collector that never stops the world, your service can scale and stay lightning fast with ease.
- Using the
Lustre
Web Framework:Lustre
is web framework with anElm-inspired architecture
for managing state. It features a declarative, functional API for constructing HTML. No templates, no macros, just Gleam. While it is primarily used for building SPAs, it easily allows you to build fullstack applications withSSR
(server-side rendering) thanks to its declarative API for constructing HTML. Additionally, Lustre featuresManaged Side Effects
that allow managing asynchronous operations like HTTP requests and timers can be tricky when JavaScript is single-threaded. Lustre provides a runtime to manage these side effects and let them communicate with your application using the same messages as your update loop.
- Using an in-memory cache system: This cache behaves very similarly to
Redis
, although it is more simplified. Its multi-process implementation is easily created thanks to Gleam's efficient implementation of theErlang Actor model
and which theBEAM virtual machine
executes brilliantly.
To test the application, you don't need to have Gleam installed on your machine. But as a minimum requirement, you do need to have Docker and NodeJs/NPM. The latter is because, although the Lustre framework's development tools download a Tailwindcss binary automatically, in this application we use a Tailwind plugin, DaisyUI
, a component library for Tailwind, and its installation requires NPM. So first of all, run this in the root of your project directory:
$ cd client/ && npm i && cd ..
Next you will need to create a Docker image of the application to run locally. Simply run this in the terminal (Docker required):
$ make prepare-deploy
This will create a Docker image (named test
) with which you can create a container that when running will listen on http://localhost:8000
.
If you intend to edit the application code or simply learn Gleam, Wisp or Lustre you will obviously need to have Gleam installed on your machine (see installation instructions here). Gleam also has support for the most common text editors (VSCode
or Vim/Neovin
). For example for VSCode you can use the VS Code Gleam extension which provides syntax highlighting and use of the Gleam Language Server, which itself provides formatting, goto-definition, autocompletion, etc.
If you look at the project folder structure, it actually consists of 3 directories:
client
: where everything related to the frontend is kept. It is essentially a SPA application of the Lustre framework that follows the Elm (model-view-update) architecture pattern. Using the Lustre development tool (lustre_dev_tools) creates an HTML scaffolding (although in our case it will not be used because the backend will render it [SSR]), and it automatically downloads aTailwind
binary (if you want to use it for styling more comfortably) andEsbuild
, which is used to create the JavaScript bundle (.mjs file
). It also allows for hot reloading (although in our case we don't use it because we serve the HTML and other static from our own backend).server
: is the application backend that listens onhttp://localhost:8000
. It is built with theWisp
web backend framework and theMist
server for Gleam. We have implemented a cache system, very similar toRedis
, that avoids repeating requests to the Pokémon API (https://pokeapi.co/) and therefore improves performance. As we said, it is based on Gleam's implementation of theErlang Actor model
, which greatly facilitates the creation of multi-process applications. As we also said, the server renders the HTML (SSR
) using Lustre's declarative API. It is also capable ofhydrating
the scaffolding with the initial data (in JSON format in the HTML itself), which improves the user experience.shared
: a directory that lacks amain
function and therefore acts as a library of functions shared by bothclient
andserver
, thanks to Gleam's feature of allowingpath dependencies
.
Of course, when you build a fullstack application with Gleam, Wisp and Lustre, you can always develop the frontend and the backend separately. But if you already have them "assembled", as in this case, you will have to compile the frontend and place the resulting bundles (minified .mjs
and .css
files) in the priv
folder on the server (which can also contain other assets, such as images or fonts) and then recompile in the backend. I have simplified all this by using a Makefile and some shell scripts that automatically execute all those commands (See the documentation for Gleam, Wisp and Lustre). So if you are editing the application code (both frontend and backend) you can simply run this command in the root of the project:
$ make run-dev
Finally, if you want to deploy the application in production, it must be said that the usual way to do it, in the case of Gleam, is by creating a Docker image and hosting it on a web service provider that runs Docker. As we said at the beginning of this section, you can use the make prepare-deploy
command, but this time passing it 2 variables: address
that will modify the address within the code to which the client requests should be directed (which will be the root path [/
] of our application) and which will be the address that will provide us with the hosting service; and, on the other hand, image-name
that will be the name of the image that we will create:
$ make prepare-deploy address=https://my-app-hosting-service.com image-name=app-image
Once the image is created, we will have to host it (e.g. on DockerHub) and then link it to the project we create in our hosting service. However, services like Fly.io make things simpler, since by simply uploading our project (with its corresponding Dockerfile
to create the application image) through a useful CLI
, the application will be deployed (see the documentation here).