Saving State — Javascript and Sinatra servers

Let’s say you are making a simple web application, using Javascript. The application is for a night club owner ( based on this repo), to see how many people enter the club on one night — It does not look at people leaving. This only has two buttons — count, which will add 1 to the count — and reset. The counter.js file would look something like this :

./src/countApp.js

This application on the web would show a number, a count button, and a reset button. The HTML and CSS setup is all up to you — all that matters to interact with your interface.js file is that the HTML has got classes or ids. The interface.js would look something like this — I am using jQuery for this example — but I do invite you to try it in vanilla JS!

./interface.js

Now, if we run the HTML file it will show a page, and update the count when clicked on either one of the buttons. Sadly — if the browser is closed or reloaded, the number is gone! That would be very annoying for the owner of this club, he’ll easily lose count. Thus, we want to be able to save state. How does that work? Through an external server! We’ll have to set up a separate folder, in which we create a Sinatra server as we’ve done before with ruby.

To start, we’ll run bundle init — to create our gem file. We’ll need a couple of gems here:
• Sinatra
• Sinatra-cross_origin
• Sinatra-json
Don’t forget to run bundle install after saving the gemfile.

We also need a config file, to run our server locally using rackup. To start with, this will only require ‘./app’ — or however you want to call your app and run the name of your class. I will call it Count, to keep it nice and simple. Next, we have to set up our app.rb, in which we will be running our server. It will look something like this, to begin with. Requiring all the gems we put in our gemfile, and to be able to run both our server and our HTML file at the same time — we need to get around CORS policy.

./app.rb

CORS stands for Cross-Origin Resource Sharing — and honestly — it is absolute hell to figure out by yourself — luckily, I figured it out for you!
As you can see above, we run a few things in our before do. These don’t always have to look exactly like this, I will add some links to pages that explain more options below! I ran

Which basically tells our browser to allow requesting code from the origin — instead of ‘*’ you can give this an HTTPS as well — that will only allow requesting code from that exact origin.

This specifies which methods are allowed when accessing the resource in response to a request. This can be answered with ‘*’ again as well — which will allow everything, for this app we are only going to be using a GET and a POST request.

This is used when you are asked to indicate which HTTP headers can be used during the actual request. It can take any number of headers, and as usual, it can take ‘*’ as a wildcard.

This is to specify how our code from the server is translated to our JS file, through JSON.

Now, we should have a server that allows us to exist online and share information with our HTML file, but how do we get them to communicate? To begin with, we want to be able to save our input somewhere. To do this, we want to create a ruby file — I know this feels super useless as you just created the JS file, but believe me — you don’t have to repeat exactly what you’ve done before. We want our ruby file to look like this:

./lib/counter.rb

We use ||= to indicate that it is 0 by default, but if it knows of another number, it will return that number. Do not forget to require this file in your app.rb and your config file! That will give enormously unclear errors. We have to save this in our get route as well and tell it to translate to JSON — quite simply, that will look something like

Using JQuery, we want to now tell our interface.js to read this information from the server. Let’s say we put our server on localhost:4567, our request would look something like this:

parseInt takes the string argument that is given to it, and turns it into an integer — I know what you think, we passed it an integer in our server, right? Yes. But JSON turned that into a string. Don’t forget to use parseInt — otherwise, it will error on NaN (not a number) and our JS app will not know what to do with it.

Try running it. Does it do anything yet? No? That is because we are not updating it! Try changing the default to another number, that works, right? To get our number to update when we count, we will need to create a post route as well. Let’s start by adding an update function to our ruby file:

./lib/counter.rb

This will save the updated number to our server. In our post route we will do the following:

But where does this param come from? It is not like we did in our ruby, where it gets its params from an erb input — it has to get it from a post in our interface.js

Do this inside of your response to a click on the count button, after the JS file count is updated. You don’t need to do anything with the function — it knows what to do now.

Let’s run this again. Does it work now? Yes! Perfect. But our reset button does not save state yet! Do you know how to solve that? Go ahead!

Now try it on other pieces of code, add more stuff to the server — go crazy!

The completed repo can be found on my GitHub.

Resources I have used can be accessed here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
https://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
https://web.dev/cross-origin-resource-sharing/

My journey through Makers academy

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store