How to Make a Reactive To-do App With Clio
I’ve been working on Clio for about 6 months now. Clio is a modern programming language targeting the cloud. It makes it easy to create distributed and decentralized applications. Clio has built-in support for remote calls, microservices, and reactive data sources. It compiles to JavaScript and can be used both on the server and the browser.
A few days ago, I announced the project publicly and decided to make a todo app using the current version of Clio in order to showcase the features of the language. This todo app uses Clio microservices to transfer data between its client and server side. It is fully reactive and all data changes on the server will be pushed to the client. Here’s how the todo app looks like and behaves:
To run this example you’ll need rethinkdb
installed. In this blog post, I assume you’re familiar with the basic concepts of Clio and its syntax, if not you can refer to the introduction blog post. Let’s see together how to make this todo app in Clio!
The Server
Let’s first start by the server-side. In Clio, functions are isolated and each function can be considered as a microservice. Clio provides a host
utility you can use to host your functions, event emitters and variables on the network. This utility will make them directly available to clients when they import them. You can even write your functions in another programing language and import them in your Clio code. It’s also possible to import Clio functions in code written in other languages. You can find an example of how to create Python microservices and import them in Clio in the Clio GitHub repository.
When you host a Clio file, Clio makes a cluster with the specified amount of workers and starts listening for connections. Connections can be made using http[s] and ws[s] protocols. The Clio remote call protocol is a work in progress that defines how these functions can be exported, imported and called.
The created workers don’t have a shared state. To share data between workers I decided to use Rethinkdb because it makes it super easy to get a change feed of our data. Alternatively, if persistence isn’t important Kafka or RabbitMQ can be used to share state between workers.
Clio can import .js
or .clio
files from a relative path, or from the project virtual environment. In Clio, virtual environments are built-in. Even though it’s still a work in progress, they are already usable. Simply installing the Rethinkdb NPM package and importing its main file would work, but instead, I decided to make a wrapper library and install it in the project virtual environment in order to better manage dependencies.
As you can see in the above screenshot, we use NPM to install Rethinkdb in node_modules.
Next we import (and export) the module in index.js
and finally we import it from index.js
in the rethinkdb.clio
file. I didn’t import it directly in case I later need to modify a part of this library (to add better compatibility with Clio or fix conflicts). This way I can simply do it in the index.js file we created.
Clio modules can be imported in the browser as well, however, that doesn’t guarantee they will work. This is because there is no require function in the browser to import other JavaScript files, and the server-side JavaScript code will not work in the browser.
Now we can import the Rethinkdb library in our host.clio
file, and connect to our clio
database:
In Clio, we use the pipe operator ->
to call a function, and we use =>
to assign the result to a variable. As you can see in the above screenshot these function calls and assignments can be chained. In Clio, these chains are called flows. They always start with data or some data sources, and they can contain several function calls or assignments.
On the server-side, Clio first tries to find the function in the local scope, if not found it checks whether there exists a builtin with that name and if that fails it will try the flow data object methods. On the browser, it first tries the local scope, then the builtins, then the window and finally it will try the object methods.
In the above code, we connect to our local Rethinkdb instance. We then select the clio
database and assign it to variable named database
. Next we choose the todos
table and assign it to the todos
variable. There are two types of string in Clio. The first one is defined between single quote signs (for example 'string'
) . The other one is defined after a #
sign and contains only a single word. It starts and ends with a white space character (for example #Hello
).
Still in the host.clio
file, let’s define a function to get all the todo items in our database:
By default, all defined functions are lazy, but we can use the @eager
decorator to make them eager. All functions should have at least one argument. In this example we used a timestamp
variable, but to keep things simple we didn’t actually use it in the function body. Clio being a functional programming language, Clio functions are functions in the mathematical sense. This means that functions always have an input and an output.
The get_todos
function runs our todo
query, gets the result cursor and calls its toArray
method which returns an array of todo items. In Clio, returns are implicit and functions return the last evaluated expression.
Let’s add a few more functions for updating a todo item, adding a todo item and removing a todo item:
Now it’s time to set up our event emitter. Let’s create an emitter
and a function that gets the data and emits it:
In the above example, we create an emitter by piping a string (the name or id of the emitter) to the built-in emitter function. The emit_data
function takes the data as argument, emits a change
event and sends the data using our todo_emitter
. Now we need to listen to the changes in our database and emit these changes using our emitter, so our clients can receive the latest version of the todo items stored in the database. This is done using the changes
method of the Rethinkdb query object todos
:
Now let’s host this file, and use these functions and our emitter as microservices! To do that, we need to create a host variable in our host.clio
file:
In the configuration, we can specify the port (defaults to 3000), exports (functions, variables, and emitters to make available as microservices) and workers (defaults to the number of CPUs). Now, if we use the host utility, we get:
That’s it! We’re done for the server-side.
The Client
Let’s move to the client-side. We are going to use jQuery and Handlebars to render elements and update them but you can use any other libraries you want, you can mix Clio and JavaScript! Let’s load jQuery, Handlebars and the clio.js
library in the index.html
file:
To fix conflicts caused by the name resolution order (local scope, then builtins, then window and then methods) we need to rename some of our jQuery methods, and then call clio.process_scripts()
to process all Clio scripts on the todo webpage:
Let’s also add a few DOM elements and some stylesheets to our page:
We then create a simple Handlebars template in our html file:
In order to import our Clio client file, we make a Clio script tag:
Next, we edit client.clio
and import the microservices that we created on the server-side:
We can import functions using http[s]
or ws[s]
, but emitters can only be imported using the ws[s]
protocol because emitters provide a stream of data, and streaming is only supported by WebSockets. Let’s make a function to add a new todo item and bind it to our #add-todo
button:
We call preventDefault
on our event to prevent the default click behavior of the browser which is to follow links. Instead our event sends a request to add_todo
. Notice that in the above function, we are using the microservice add_todo
we created in the server-side just like a regular function! We then use $
(jQuery) to select our #add-todo
button and listen to its click events. Now we compile our handlebars template and assign it to a variable named template
:
We still need a few functions and event handlers. One is for updating a todo item on the server if the user modifies it on the browser:
In the do_update_todo
function, we first get the target of the event which we pipe to jQuery in order to select the parent element (which is our todo item element), and store it in a variable named $todo
. We then add the editing
class to it. We do this because when we receive data updates from the server, we need to rerender todo items with their updated data. However we should make sure we don’t replace an element with its updated version while the user is editing it. In the next lines, we get the .title
, .body
, .status
and data-todo-id
of this element and pass them to our update_todo
function. We also define a function to remove the editing
class which we will call when the user is done modifying a todo item.
We now define a function for removing a todo item that exists on the server:
Let’s set up some DOM event listeners and react to them. For example when a todo item is removed or modified, we want to call the corresponding functions:
In the above code, we define some global event listeners. If any .remove
button is clicked, we call do_remove_todo
. If we have an input
event in any .todo
element, we call do_update_todo
. Finally, if we have a focusout
event for any .todo,
we remove the editing class.
We also need a function that updates our rendered todos if they’re modified on the server, for example when another user modifies the todo items:
The above function receives a todo item as an argument. If our todo item has old_val
and new_val
values, it means it was modified. If it only has new_val
it means it is new, and if it only has old_val
it means it was removed.
If we got new todo item, we simply pass its new value to our template, then to jQuery (to make a new element) and then append it to our todos container. If it was removed, we use data-todo-id
to select it and remove it. The cat
function is a Clio built-in function that joins strings together. Finally, if we got a modified todo item, we select it, check if it has the editing
class, and if not, we replace it with the new value.
One last step is to react to changes on the server, and actually render the current existing todo items:
In Clio, we use the map operator ->*
(or -> *
) to map the result of a function to another function. In the above code, we listen to the change
events of our todo_emitter
and update the page using the update_page
if there are any changes. In the next flow, we call the get_todos
function, map the template
function to the result, then map jQuery and then append them to our todos container one by one.
The Result
Now if we host the index.html
using a simple http-server
(live-server
or python -m http.server
for example), we can see our fully reactive todo app in the browser:
I hope you liked this tutorial and that I got some of you interested in the Clio programming language. Feel free to take a look at our roadmap, website, documentation and GitHub repo. If you like Clio, it would be extra nice if you could give star us on GitHub! You can check the full source code of the todo app here. If you would like to contribute to Clio, you can check our contribution guide.