Kinto wordmark

How to develop a Web App with Kinto

01 December 2016

This post was originally published in French on Enguerran Colson's blog.

Our goal

Hi folks, today, we are going to create a simple Web App that allows visitors to chat in a room accessible from a public URL.

KintoChat, our instant chat Web App

What will we need?

  • A really simple chat Web App with minimal UI.
  • We will need a bit of JavaScript to enhance and animate interactions within the application and we will also need a way to store and share the app's data. Ideally, we would like our application to consume a REST API exposing endpoints like https://api.discute.re/v1/rooms/12345/messages, where discute.re is the domain of our application (discutere means to chat in Latin).

Our API endpoints would be:

  • /rooms to return all chat rooms,
  • /rooms/:id/ to return a particular chat room identified by an ID,
  • /rooms/:id/messages to return all messages from a particular room.

To post a message, our front-end will do a POST request to /rooms/:id/messages.

We would store each message like this:

{
    "message": "I know nothing",
    "last_modified": 1480340246147,
    "author": "Jon Snow",
    "id": "a1e6e879-5c94-4a01-9ec2-28e152d1d8f4"
}

The JavaScript code could use whatever library or latest hype JS framework but in our example we will stick to modern Vanilla JS and use top notch ES2015 syntax.

API creation

Hmm… what backend should we use? Django? Express? Firebase?… oh wait, why don’t we give Kinto a try?

Kinto provides a flexible and generic enough API to address the needs of many different kinds of application. Call us lazy, effective or pragmatic but Kinto will prevent us to write our own API and help us handle other APIs.

Let's see how to use Kinto for our instant chat Web App.

To show some pride, we will use an explicit subdomain for our main URL https://kinto.discute.re/v1 to show our configuration rather than an agnostic https://api.discute.re/v1.

Using Kinto, here are the native API endpoints we will use to match our application requirements:

  • /rooms/4815162342 becomes /collections/4815162342;
  • /rooms/4815162342/messages becomes /collections/4815162342/records;

As we have seen before, we can add a new post to our backend with a simple POST request /collections/4815162342/records.

A Kinto server can handle many Web applications through the concept of buckets; a bucket can contain many collections, a collection can contain many records, and records can contain arbitrary JSON data.

For our application, we’ll create a chat bucket, which will contain as many collections as we want distinct chat rooms. Each room collection will itself represent chat messages through records.

You can use the Kinto Admin to visually browse and manage your application data.

Our records in Kinto Admin

Considering a chat room with the id 4815162342, we could:

  • retrieve all messages for that 4815162342 room by requesting GET https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records;
  • post new messages to the room using POST https://kinto.discute.re/buckets/chat/collections/4815162342/records.

It's very similar to our first envisioned API calls, which were:

  • GET https://api.dicute.re/v1/rooms/4815162342/messages,
  • POST https://api.dicute.re/v1/rooms/4815162342/messages.

Let’s install Kinto

There are many ways to install (or deploy) Kinto:

If you want to play with Kinto, an ephemeral instance is also available on https://kinto.dev.mozaws.net/v1/.

Once deployed, we have access to our https://kinto.discute.re/v1 default URL that returns:

{
    "project_version": "5.0",
    "settings": {
        "readonly": false,
        "batch_max_requests": 25
    },
    "capabilities": {
        "admin": {
            "description": "Serves the admin console.",
            "version": "1.5.0",
            "url": "https://github.com/Kinto/kinto-admin/"
        },
        "default_bucket": {
            "description": "The default bucket is an alias for a personal bucket where collections are created implicitly.",
            "url": "https://kinto.readthedocs.io/en/latest/api/1.x/buckets.html#personal-bucket-default"
        }
    },
    "project_docs": "https://kinto.readthedocs.io/",
    "project_name": "kinto",
    "http_api_version": "1.12",
    "url": "http://kinto.discute.re/v1/"

}

And that's it! Now we can move to the front-end development phase.

Viva JavaScript!

In order to get messages, we will have to write a bit of code to get the room's id through the application URL:

// https://discute.re/#4815162342  is mapped to the collection 4815162342
const collectionName = window.location.hash.slice(1);

Then we need to do a GET request to get the list of all room's messages. A simple GET https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records would do the job (with fetch or XMLHttpRequest), but we will rather be using the kinto-https-js library that offers a clean client API, take a look at this code:

const kintoUrl = 'https://kinto.discute.re/v1';
// options stores the connection identifiers
const client = new KintoClient(kintoUrl, options);
client.bucket('chat')
  .collection(collectionName)
  .listRecords()
  .then(({data}) => displayMessage(data));

The options variable contains at least the authentication token. This token was not created before. Kinto accepts all requests and responds accordingly to the token: if the token gives access to the resource, the user can act on it (create, update, etc.), otherwise he can't. Read more about authentication here.

displayMessage(data) can now retrieve the JSON object and render it as HTML.

[
    {
        "message": "I know nothing",
        "last_modified": 1480340246147,
        "author": "Jon Snow",
        "id": "a1e6e879-5c94-4a01-9ec2-28e152d1d8f4"
    },
    {
        "message": "I know that I know nothing",
        "last_modified": 1480340396521,
        "author": "Socrates",
        "id": "94787c48-aa96-4121-879f-1bd68d3c1f23"
    }
]
function getMessage(message, author) {
    return
    `<li>
       <span>
         <span>${author}</span>
         <span>${message}</span>
       </span>
     </li>`;
}

function displayMessage({message, author}) {
  var container = document.createElement('div');
  container.innerHTML = getMessage(message, author);
  messagesList.firstChild.appendChild(container.firstChild);
}

Here is how the HTML code looks like:

<div id="messages">
  <ul>
    <li>
      <span>
        <span>Jon Snow</span>
        <span>I know nothing</span>
      </span>
    </li>
    <li>
      <span>
        <span>Socrates</span>
        <span>I know that I know nothing</span>
      </span>
    </li>
  </ul>
</div>

Post a new message

This time, we only have to send the form's data to the Kinto server.

Once again, we could go with a simple POST request https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records but here we will use again the Kinto client library to write:

const messageInput = document.getElementById('message');
const messageForm = document.getElementById('message-form');
messageForm.addEventListener('submit', saveMessage);

function saveMessage(e) {
  e.preventDefault();
  if (messageInput.value) {
    client.bucket('chat')
      .collection('4815162342')
      .createRecord({
        author: 'Jean Neige',
        message: messageInput.value
      });
  }
}

Front-end enhancements

Finally to fancy up our little chat application, we use:

  • getmdl.io to syle the components;
  • pusher.com to automatically update our app when new messages arrive.

With these modifications and without having to write any backend code, we have our chat Web App accessible at this URL: https://enguerran.github.io/kintochat/#4815162342.

The example source code is available on Github, please keep in mind that it is just for educational purpose, it’s not production ready, it’s not even covered with tests.

N.B.: if you prefer to use Elm instead of JavaScript, you can find an Elm client here.

But, what is Kinto?

You can think of Kinto as a generic backend to store and sync your data.

Kinto is written in Python around Pyramid. Kinto is highly configurable through its kinto.ini file and plugins.

Kinto is OpenSource and allows us to store of our user's data away from Big Five closed silos and other private cloud unicorns.

If you stumble upon a bug or want to express a need, you can easily contact the Kinto community, open an issue or create your own plugin.

Now that you know how to store and sync your Web App’s data, come and join the Kinto community, share your experience, provide us some feedback.

We would like to see more and more useful Web Apps developed with Kinto, it’s up to you now!