Une API REST Kotlin développée en 5 minutes
L’idée de cet article est d’introduire les concepts du langage Kotlin en vous présentant comment développer en cinq minutes (montre en main) une API REST basée sur ce nouveau langage de programmation, la librairie Javalin et le driver MongoDB Kmongo. En bonus (parce que les tests c’est que du bonus hein …) : un exemple de test unitaire en utilisant la librairie Kotlintest.
Qu’est-ce que Kotlin ?
Kotlin est un langage de programmation qui commence à prendre de l’ampleur dans le monde du dev. Il est maintenu par Jetbrains et s’exécute dans la JVM. Il peut également être utilisé pour du dev Android ou être compilé en Javascript.
Ce langage est typé statiquement, ce qui implique que les variables, fonctions et expressions utilisent des types prédéfinis qui peuvent être contrôlés au moment de la compilation.
Pourquoi Kotlin ?
Le but principal de Kotlin est simple : résoudre certains problèmes apparus avec le langage Java. À titre d’exemple, un programme développé avec Kotlin aura environ 40% de moins de lignes de code que le même programme écrit en Java. Sachant qu’on pourra toujours profiter de l’ensemble assez riche de librairies Java, pourquoi s’en priver ?
Mais Scala dans tout ça ?
Bonne question. Je vous renvoie vers ce post très intéressant que je résume brièvement en trois points : Kotlin est plus lisible, plus facile à prendre en main et plus fortement lié à Java. Le seul petit bémol, c’est que Kotlin est encore jeune comparé à Scala. La communauté n’est donc pas aussi importante bien qu’elle commence à grandir de façon significative.
Prêt et convaincu ? Alors on démarre !
Plusieurs frameworks web connus permettent d’écrire des API REST Kotlin :
pour ne citer qu’eux. Personnellement j’ai tendance à préférer les outils légers plutôt que les grosses usines. C’est pourquoi j’ai décidé de m’orienter vers une librairie plus simple, plus flexible et plus légère : Javalin.
Son but n’est pas d’être un framework web complet mais simplement de proposer une librairie REST fluide et facile à prendre en main.
Ci-dessous les 4 dépendances dont vous aurez besoin pour créer votre API :
Les deux premières sont requises pour utiliser Kotlin et ses librairies standards, la troisième est la librairie REST et la dernière est le module Jackson de Kotlin.
Créons maintenant notre fichier qui va exposer les routes de notre API :
On vient donc d’exposer en quelques lignes : trois routes GET, une route POST, une route PATCH et une route DELETE tout ça sur le port 7000 de notre serveur. Celles-ci retournent une réponse 501 “Not Implemented” pour le moment.
L’objet ctx disponible pour chaque route est de type Context. Pour retrouver l’exhaustivité des fonctionnalités disponibles pour cet objet, je vous renvoie vers la documentation Javalin qui est plutôt complète et bien faite.
À titre d’exemple, on a aussi créé un intercepteur d’erreur 404 (ligne 14). Très pratique si on veut mutualiser une gestion d’erreur.
Persistence des données
Nous allons utiliser une base de données MongoDB avec laquelle nous allons discuter grâce au très bon driver Kmongo. Encore une fois, c’est la légèreté de la librairie qui m’a poussé à l’utiliser.
Rajoutons de ce pas la dépendance à notre pom.xml :
Ce driver me permettra d’introduire un concept de Kotlin très intéresant : Les Coroutines
Toujours expérimental, ce concept facilite le développement de programmes asynchrones et délègue toute cette gestion aux librairies.
Par conséquent, plus de gestion de Future, Deffered ou autres callbacks. Avec ce mot clef, l’asynchronisme de votre application sera totalement transparent pour vous.
Un exemple vaut mieux que plusieurs lignes d’explication :
Nous voulons récupérer un utilisateur identifié par son id ? Ça se passe comme ça :
Et oui c’est tout. Votre méthode findById retourne un User (ou null si non trouvé, d’où le point d’interrogation) DE FAÇON ASYNCHRONE. C’est le mot clef suspend qui fait le job. Ça semble un peu magique pour le moment et vous vous demandez :
Comment ça se passe pour le router ?
Reprenons notre fichier UserRouter.kt pour lier notre route GET /users/:id à notre méthode findById de notre DAO. L’id utilisateur fourni dans le path de la requête est récupéré ligne 26 grâce à l’instruction : ctx.param(“id”)!!
Notre fichier devient :
Plusieurs choses sont intéressantes ici :
- L’utilisation du block runBlocking
Ce bloc permet de demander à notre programme d’attendre que les instructions qu’il contient se terminent. Ce mot clef est donc extrêmement important car c’est lui qui va définir les points de synchronisation de votre application.
Ici le cas est simpliste, on attend de recevoir l’object User avant de répondre le résultat au client REST. Mais lorsque des cas plus compliqués se présenteront, vous pourrez combiner les appels asynchrones dans un même block runBlocking et ainsi paralléliser vos traitements, puis retourner un résultat synchrone.
Grâce à ce block et l’utilisation du mot clef suspend, nous avons rendu notre programme asynchrone de façon quasi transparente. - L’utilisation du block let
Ce mot clef fait partie de le librairie standard de Kotlin et est très utile pour tester (entre autre) si une variable est nulle en rendant votre code bien plus lisible.
Je vous renvoie vers cet article qui détaille let ainsi que d’autres fonctions intéressantes de la librairie standard Kotlin.
Ce que nous avons réalisé nous, c’est
- Retourner l’objet User si celui-ci n’est pas null
- Retourner une 404 si le document User n’est pas trouvé en base
Bonus time !
Pour terminer, je vous propose de développer un simple test unitaire en utilisant une libraire ultra flexible et très légère (vous commencez à me connaître maintenant) : Kotlintest
Rajoutons une dernière dépendance à notre pom :
On crée une simple classe de spec qui va tester notre modèle User et notre méthode isAdmin()
Voici notre classe User :
Et notre classe de test :
Conclusion
J’espère qu’on aura ensemble réussi à démystifier Kotlin et son écosystème.
Clairement, la vocation de ce langage est d’offrir une alternative à Java qui soit facile à prendre en main, efficace et très lisible. De mon point de vue, c’est cette lisibilité du code qui était et est toujours le point noir de Scala.
J’ai essayé de vous transmettre également ma philosophie :
« Une force moyenne s’exprime par la violence, une force suprême s’exprime par la légèreté. »
Trouver des librairies légères, flexibles, simples à prendre en main et qui répondent (uniquement si possible) à votre besoin c’est pour moi très important.
Le code du projet complet est disponible sur le Github d’InTech.
N’hésitez pas à me contacter sur mon Twitter si vous avez des remarques, suggestions ou questions.