Tutoriel CouchDB #1 publié le lundi 14 décembre 2015 17:09

CouchDB est un serveur de base de données (documents JSON) avec une interface HTTP.

À la fin de ce tutoriel, vous afficherez une page html complète sur votre serveur CouchDB en mode local. Seul la commande curl et un fureteur sont requis, au delà de l'installation.

Installer CouchDB

Disponible dans Ubuntu. Téléchargez exécutables pour Windows, Mac OS X ou les sources: http://couchdb.apache.org/#download

Compilez et installez sur Debian: https://cwiki.apache.org/confluence/display/couchdb/Debian

Pour les autres plateformes, voir https://cwiki.apache.org/confluence/display/couchdb/Installing+CouchDB

Admin party et localhost

Une fois CouchDB installé et démarré, le serveur est disponible à l'adresse http://localhost:5984/ (ou http://127.0.0.1:5984/ ) en mode Admin Party. Ça signifie que toutes les opérations sont permises en autant qu'elles proviennent de la machine locale. Les prochains tutoriels expliqueront les différents niveaux de sécurité et d'accès.

Premières interactions

Ouvrez un terminal et tapez:

$ curl http://localhost:5984/
{"CouchDB":"Welcome","uuid":"1edb4c20665999e29f0f9bb91a4fb21e","version":"1.6.1","vendor":{"name":"The Apache Software Foundation","version":"1.6.1"}}

La version affichée dépendra bien sûr de la version que vous avez installée. Si vous obtenez une erreur, c'est que l'installation ne s'est pas bien complétée, que le serveur n'est pas démarré ou encore parce que les options par défaut sont différentes.

Si CouchDB n'est pas disponible sur le port 5984, vous obtiendrez une erreur du genre:

$ curl http://localhost:5984/
curl: (7) Failed to connect to localhost port 5984: Connexion refusée

Une première base de données

Pour obtenir les informations sur une base de données, dans ce cas-ci "madb", tapez:

$ curl http://localhost:5984/madb
{"error":"not_found","reason":"no_db_file"}

Parce "madb" n'existe pas encore, vous obtiendrez cette erreur. Puisque CouchDB supporte le protocol HTTP, le serveur retourne aussi un code d'erreur 404 (not found):

$ curl -i http://localhost:5984/madb
HTTP/1.1 404 Object Not Found
Server: CouchDB/1.6.1 (Erlang OTP/18)
Date: Sun, 13 Dec 2015 02:09:55 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 44
Cache-Control: must-revalidate

{"error":"not_found","reason":"no_db_file"}

Par défaut, la commande curl utilise la méthode HTTP GET. Pour créer la base de données "madb", utilisez la méthode PUT:

$ curl -i -X PUT http://localhost:5984/madb
HTTP/1.1 201 Created
Server: CouchDB/1.6.1 (Erlang OTP/18)
Location: http://localhost:5984/madb
Date: Sun, 13 Dec 2015 02:16:57 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Cache-Control: must-revalidate

{"ok":true}

L'option -i retourne les headers HTTP, remarquez le code 201 qui indique la création de "madb". On peut le confirmer:

$ curl -i http://localhost:5984/madb
HTTP/1.1 200 OK
Server: CouchDB/1.6.1 (Erlang OTP/18)
Date: Sun, 13 Dec 2015 02:20:19 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 223
Cache-Control: must-revalidate

{"db_name":"madb","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1449973017174440","disk_format_version":6,"committed_update_seq":0}

Le code 404 devient un code 200 (OK) une fois que la ressource existe, comme on s'y attend.

Utilisez la méthode DELETE pour effacer une ressource, tel que "madb":

$ curl -i -X DELETE http://localhost:5984/madb
HTTP/1.1 200 OK
Server: CouchDB/1.6.1 (Erlang OTP/18)
Date: Sun, 13 Dec 2015 02:23:13 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Cache-Control: must-revalidate

{"ok":true}

Notez qu'il n'y a pas de undo.

Recréez "madb" en laissant tomber le -i (l'affichage des headers):

curl -X PUT http://localhost:5984/madb
{"ok":true}

Un premier document

Jusqu'ici, vous avez créé une base de données vide. Rien de bien excitant vous dites-vous, mais il y a plus. Vous savez maintenant comment créer (PUT), obtenir (GET) et détruire (DELETE) une ressource telle qu'une base de données. Ceci s'applique aussi bien aux documents eux-même.

Mais d'abord, une petite parenthèse sur le format JSON.

JSON

Les documents sont dans le format JSON, c'est à dire des objets JavaScript où les clés et les valeurs sont toutes encadrées par des guillemets ("), sauf pour les nombres et true/false.

{
  "titre": "Mon premier document",
  "timestamp": 1449980383347,
  "commentaires": [
    "merveilleux",
    "fascinant"
  ],
  "auteur": {
    "nom": "Robin",
    "email": "robin@example.com"
  }
}

Dans l'exemple précédent, la valeur de la clé auteur est elle-même un object avec deux clés. La clé timestamp représente le nombre de millisecondes depuis le 1 janvier 1970, le UNIX Epoch. On aurait aussi pu choisir une chaine de date ISO 8601 comme "2015-12-13T04:19:43.347Z". C'est d'ailleurs un format préféré qui met la lisibilité devant l'espace de stockage. Dans les deux cas, on pourra ordonner les valeurs sans dépendre de la langue (format déconseillé: "Sat Dec 12 2015 23:19:43 GMT-0500 (EST)").

Vous pouvez utiliser n'importe quelle chaine comme clé. Remarquez aussi la valeur de la clé commentaires, encadré par des [] qui indique un tableau de valeurs. Ici ce sont des chaines mais ça pourrait être des nombres, booléens, des objects ou même des tableaux.

Notez finalement que les valeurs sont ordonnées dans les tableaux mais que l'ordre des clés n'est pas garanti.

Le document dans la base de données

Pour commencer, ce document simplifié:

{
  "titre": "Mon premier document",
  "dateheure": "2015-12-13T04:19:43.347Z"
}

On peut l'écrire sur une ligne, l'indentation n'a pas d'importance:

{"titre":"Mon premier document","dateheure":"2015-12-13T04:19:43.347Z"}

Comme pour la création d'une base de données, on utilise la méthode HTTP PUT mais en plus de l'URL, on veut aussi passer un contenu (le document JSON). Il faut indiquer le format (en mimetype) au serveur en plus du document.

$ curl -i -X PUT -H "content-type: application/json" -d'{"titre":"Mon premier document","dateheure":"2015-12-13T04:19:43.347Z"}' http://localhost:5984/madb/premier-doc
HTTP/1.1 201 Created
Server: CouchDB/1.6.1 (Erlang OTP/18)
Location: http://localhost:5984/madb/premier-doc
ETag: "1-98068163c61c5b82a8f2c9066601f5f2"
Date: Sun, 13 Dec 2015 23:16:58 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 74
Cache-Control: must-revalidate

{"ok":true,"id":"premier-doc","rev":"1-98068163c61c5b82a8f2c9066601f5f2"}

On vient de créer le document "premier-doc" dans la base de données "madb". On peut maintenant l'obtenir avec un HTTP GET:

curl http://localhost:5984/madb/premier-doc
{"_id":"premier-doc","_rev":"1-98068163c61c5b82a8f2c9066601f5f2","titre":"Mon premier document","dateheure":"2015-12-13T04:19:43.347Z"}

En plus des champs titre et dateheure, le document comporte maintenant deux autres clés, _id et _rev. Les clés qui commencent par "_" sont réservés à CouchDB. La valeur de _id vient directement de l'URL tandis que la valeur de _rev est utilisée à l'interne pour la réplication et les mises à jour entre autres.

Mettre à jour un document

Pour ajouter un champ tags à l'exemple précédent, soit un tableau de mots-clés, on va modifier le document ainsi:

{
  "_id": "premier-doc",
  "_rev": "1-98068163c61c5b82a8f2c9066601f5f2",
  "titre": "Mon premier document",
  "dateheure": "2015-12-13T04:19:43.347Z",
  "tags": ["accueil", "personnel"]
}

Cette fois, au lieu de passer le contenu (JSON) du document comme argument de curl, on le mettra dans un fichier "premier-doc.json" et on utilisera ce nom de fichier comme argument. Pour le serveur CouchDB, ça revient au même, il s'agit uniquement de l'interface du client curl qu'on utilise pour les exemples.

$ curl -X PUT -H "content-type: application/json" -d@premier-doc.json http://localhost:5984/madb/premier-doc
{"ok":true,"id":"premier-doc","rev":"2-2b5984495aa9086f131c8102b3cae868"}

Maintenant quand on demande le document on notera que la révision a changé et que le nouveau champ tags est bien là:

curl http://localhost:5984/madb/premier-doc
{"_id":"premier-doc","_rev":"2-2b5984495aa9086f131c8102b3cae868","titre":"Mon premier document","dateheure":"2015-12-13T04:19:43.347Z","tags":["accueil","personnel"]}

CouchDB: plus qu'un serveur de données

Jusqu'ici, c'est assez standard. On a le bon vieux CRUD: Create, Retrieve, Update et Delete et une interface HTTP. On peut créer et lire des documents JSON. Mais encore?

Design document: l'application

C'est le temps d'introduire ce qu'on nomme les Design documents. Tous les documents dans CouchDB sont dans le format JSON. Les Design documents ne font pas exception. Ils se distinguent des autres documents parce qu'ils représentent une application, c'est à dire comment serviront les documents, définir les index, etc.

Si les documents en général peuvent avoir à peu près n'importe quel ID, les ID des Design documents commencent tous par la chaine "_design/". En général, un seul Design document (souvent "_design/app") est utilisé par base de données, mais il est permis d'en avoir plus. On pourrait par exemple utiliser un ensemble de données de deux façons complètement différentes et indépendantes.

Avec une première fonction du Design document, on pourra enfin afficher la page web promise en début de ce tutoriel, on non pas seulement échanger du JSON.

Fonction show(doc)

Le Design document va ressembler à ceci:

{
  "language": "javascript",
  "shows": {
    "page": "function (doc) {return '<html><head><title>' + doc.titre + '</title></head><body><h1>' + doc.titre + '</h1><ol>' + doc.tags.map(function(tag){return '<li>' + tag + '</li>';}).join('') + '</ol></body></html>';}"
  }
}

On va le mettre dans le fichier "ddoc.json" pour le moment.

Les fonctions show vont sous la clé shows. Ce n'est pas négotiable. On va appeler la fonction "page", et la définir comme une chaine qui contient une fonction JavaScript. On peut écrire les fonctions avec différents langages, tel qu'Erlang (le langage natif de CouchDB) ou bien JavaScript comme c'est fait ici. La clé language indique le langage qu'on utilisera pour l'ensemble de ce Design document.

Les fonctions "show" comme "page" ici transforme un document de JSON vers le format de notre choix. Notez par contre que CouchDB n'offre aucun format de conversion prédéfini.

La fonction prend au moins un argument, le doc en question. Ici on construit une longue chaine à partir des champs titre et tags. Il n'y a pas d'obligation d'utliser tous les champs. Comme la valeur du champ tags est un tableau, on utilise la fonction Array.map() pour faire une liste ordonnées à partir des chaines de tags. On verra comment utiliser des librairies JavaScript pour faire des templates et bien plus (par exemple, transformer un champ en format markdown vers du html), mais ce sera pour un des prochains tutoriels.

Il est temps de mettre le fichier "ddoc.json", le Design document, dans la base de données. On va utiliser l'ID "_design/app":

$ curl -X PUT -H "content-type: application/json" -d@ddoc.json http://localhost:5984/madb/_design/app
{"ok":true,"id":"_design/app","rev":"1-f2887f3d4cc17f945b7d1d5b5d9d7691"}

C'est exactement la même méthode HTTP PUT et le même content-type qui est utilisé que pour les autres documents. Une fois que la fonction "page" qu'on vient de définir existe dans le Design document, on peut enfin obtenir la page web promise:

$ curl -i http://localhost:5984/madb/_design/app/_show/page/premier-doc
HTTP/1.1 200 OK
Vary: Accept
Server: CouchDB/1.6.1 (Erlang OTP/18)
Etag: "X93MZYERXCUGDZWBHMSCPTCL"
Date: Mon, 14 Dec 2015 07:07:20 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 147

<html><head><title>Mon premier document</title></head><body><h1>Mon premier document</h1><ol><li>accueil</li>,<li>personnel</li></ol></body></html>

On obtient bien le code 200 OK, le content-type text/html (utf-8) et puis le contenu généré à partir du document "premier-doc".

On va décomposer l'URL http://localhost:5984/madb/_design/app/_show/page/premier-doc pour bien comprendre.

On peut avoir plusieurs fonctions shows selon ses besoins. Il existe d'autres types de fonctions que show, ce sera aussi pour un tutoriel futur.

Prochaines étapes

La première chose à faire serait de créer un autre document avec les champs titres et tags et le ID de votre choix pour le convertir en HTML à son tour et voir que tout fonctionne comme promis. Appelons ça un exercise du lecteur. Sentez-vous bien à l'aise d'adapter la fonction page, peut-être ajouter un champ contenu aux documents et l'afficher en plus du titre?

Vous avez maintenant un serveur web (HTTP/HTML) et une méthode de base mais en même temps universelle pour créer, mettre à jour et effacer des documents. Vous savez aussi comment faire une fonction pour convertir un document JSON en HTML.

Dans le prochain tutoriel, vous apprendrez comment faire une liste de documents dans l'ordre de votre choix et aussi comment l'afficher sous forme de HTML. Les autres tutoriels couvriront les utilisateurs (et les rôles), la sécurité (les accès), des URL plus jolis (rewrites), les fichiers attachés aux documents, comment faire un serveur web public, la réplication, les fils de changements, les outils de développement et bien plus.