Développer une WebApp avec Kinto
Le besoin
Partons du principe que nous voulons créer une application de discussion instantanée (chat) permettant au détenteur d'une url définissant une room de discuter avec l'ensemble des participants.
Nous aurons besoin d'un peu de JavaScript pour animer l'application et d'un backend permettant de stocker et partager les données. De cette manière, notre frontend pourra consommer une API de la forme https://api.discute.re/v1/rooms/4815162342/messages
avec discute.re
le domaine de notre application (discuter se dit dicutere en latin), /rooms
renvoie la liste des salles (une discussion par salle), /rooms/:id
renvoie les informations concernant la salle identifiée par id et /rooms/:id/messages
renvoie la liste des messages de la room id.
Pour publier un message, notre frontend fera une requête POST /rooms/:id/messages
.
De manière très simple, chaque message sera de la forme :
{
"message": "Je ne sais rien",
"last_modified": 1480340246147,
"author": "Jean Neige",
"id": "a1e6e879-5c94-4a01-9ec2-28e152d1d8f4"
}
Le code JavaScript peut utiliser toutes les bibliothèques ou frameworks du moment, mais dans la suite, nous utiliserons du Vanilla JS avec une syntaxe ES2015.
Création de l'API
Django ? Express ? Firebase ? … Kinto
Kinto répond tout à fait au besoin de cette application. Une fois installé, Kinto donne accès à une API permettant de créer et manipuler d'autres API.
Voyons comment utiliser Kinto avec notre application de discussion instantanée. Tout d'abord l'url https://api.discute.re/v1
deviendra https://kinto.discute.re/v1
(je rends le sous-domaine explicite afin de bien comprendre la configuration).
Puis les différentes routes :
/rooms/4815162342
devient/collections/4815162342
;/rooms/4815162342/messages
devient/collections/4815162342/records
;
Et de la même manière que précédemment, pour ajouter un nouveau message à notre backend, nous ferons POST /collections/4815162342/records
.
Comme une instance de Kinto peut servir à plusieurs applications, Kinto nous permet de stocker les collections de notre application de discussion (nos collections sont les différentes rooms créées par les utilisateurs) dans un seau qu'on appellera chat, ce qui s'écrira /buckets/chat
.
Voilà donc la requête qui permettra de récupérer l'ensemble des messages de notre room d'exemple : GET https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records
et pour poster un nouveau message POST https://kinto.discute.re/buckets/chat/collections/4815162342/records
.
N.B. : C'est tout de même très proche de notre première idée d'api qui était GET https://api.dicute.re/v1/rooms/4815162342/messages
et POST https://api.dicute.re/v1/rooms/4815162342/messages
.
Installation de Kinto
Il existe plusieurs manière d'installer (ou de déployer) Kinto : sur heroku, avec docker, ou encore avec pip.
Une instance éphémère est disponible sur le serveur https://kinto.dev.mozaws.net/v1/.
Une fois le déploiement fait, nous avons accès à l'url de base https://kinto.discute.re/v1
qui répond :
{
"project_version": "5.0.1.dev0",
"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/"
}
Et c'est tout ! On passe ensuite au développement du frontend.
Le JavaScript c'est pratique
Récupérer les messages
Nous devons écrire du code qui permet de récupérer l'id de la room via l'url de l'application :
// https://discute.re/#4815162342 correspond à la collection 4815162342
this.collectionName = window.location.hash.slice(1);
Ensuite, il suffit de faire une requête GET
pour récupérer la liste des messages de la room, un simple GET https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records
serait suffisant, mais nous allons préférer l'utilisation d'une bibliothèque d'API, kinto-http.js, ce qui donne le code suivant :
const kinto_url = 'https://kinto.discute.re/v1';
// options contient les identifiants de connexion
const client = new KintoClient(kinto_url, options);
client.bucket('chat')
.collection('4815162342')
.listRecords()
.then(({data}) => displayData(data));
N.B. : la variable options
contient au moins le token d'authentification. Ce dernier n'a pas été créé au préalable, Kinto accepte toutes les requêtes et adapte sa réponse en fonction du token : si le token donne accès à la ressource, l'utilisateur peut agir dessus (création, modification, etc.), sinon non. Pour en savoir plus.
displayData(data)
n'a plus qu'à récupérer l'objet json suivant et d'en faire une version html.
[
{
"message": "Je ne sais rien",
"last_modified": 1480340246147,
"author": "Jean Neige",
"id": "a1e6e879-5c94-4a01-9ec2-28e152d1d8f4"
},
{
"message": "Je sais que je ne sais rien",
"last_modified": 1480340396521,
"author": "Socrate",
"id": "94787c48-aa96-4121-879f-1bd68d3c1f23"
}
]
getMessage(message, author) {
return (
`<li>
<span>
<span>${author}</span>
<span>${message}</span>
</span>
</li>`
);
}
displayMessage({message, author}) {
var container = document.createElement('div');
container.innerHTML = this.getMessage(message, author);
this.messagesList.firstChild.appendChild(container.firstChild);
}
Et le code HTML :
<div id="messages">
<ul>
<li>
<span>
<span>Jean Neige</span>
<span>Je ne sais rien</span>
</span>
</li>
<li>
<span>
<span>Socrate</span>
<span>Je sais que je ne sais rien</span>
</span>
</li>
</ul>
</div>
Poster un nouveau message
Cette fois, il suffit d'envoyer les données d'un formulaire au serveur Kinto. Encore une fois, un simple POST https://kinto.discute.re/v1/buckets/chat/collections/4815162342/records
mais nous allons utiliser la bibliothèque d'API pour écrire :
const messageInput = document.getElementById('message');
const messageForm = document.getElementById('message-form');
messageForm.addEventListener('submit', saveMessage);
saveMessage(e) {
e.preventDefault();
if(messageInput.value) {
client.bucket('chat')
.collection('4815162342')
.createRecord({
author: 'Jean Neige',
message: messageInput.value
});
}
}
Amélioration du client
Pour finir, on ajoute quelques petites améliorations :
- utilisation de getmdl.io pour le style ;
- mise en place de Pusher.com pour la mise à jour instantanée automatique des nouveaux messages ;
N.B. : le code est vraiment très simplifié, ce n'est pas du code de production, il n'est même pas testé ! ;)
Avec l'ensemble des ces modifications et sans jamais avoir à manipuler du code backend, nous obtenons l'application disponible à cette adresse : https://enguerran.github.io/kintochat/#4815162342.
Le code est disponible sur https://github.com/enguerran/kintochat.
Alors Kinto, c'est quoi ?
Kinto est donc un service backend d'API. Il est développé en python autour de Pyramid. Kinto est hautement configurable avec son fichier kinto.ini
et un ensemble de plugins.
Il est OpenSource et a pour objectif de conserver les données de nos utilisateurs hors des silos des GAFAM et autres licornes du cloud privé.
Si un bug ou un besoin surviennent, il est extrêmement facile de joindre la communauté, d'ouvrir une issue ou de créer son propre plugin.
Maintenant que vous savez qu'il existe un moyen simple de stocker et de synchroniser les données de vos webapps, n'hésitez pas à rejoindre la communauté, à utiliser les outils et à partager votre expérience.
→ Kinto