Souscrire à la newsletter 💌

Créer un module Node.js avec TypeScript



23 Mai 2018
#angular #javascript


Créer un module Node.js avec TypeScript


Introduction


Il existe plus de 350.000 modules Node.js disponibles sur npm. Le but d'un module est de vous faire gagner du temps lors du dĂ©veloppement de votre application, en vous proposant une ou plusieurs fonctionnalitĂ©s. Dans ce tutoriel, je vous montrerai comment Ă©crire un module Node.js en utilisant TypeScript. Soyez rassurĂ©, je vous montrerai Ă©galement comment publier votre package sur npm. PrĂȘt?


Un petit bout de théorie


Et si on trouvait un exemple pour expliquer tout ceci?
Pour publier un module sur npm, on s'assure que le nom de dernier soit unique. Logique me diriez-vous! Supposons qu'ayant fini ce super cours, vous souhaitez publier un nouveau module portant comme nom: tralala.
Pour vous assurer qu'un tel module n'existe, vous devriez vous rendre sur npm et effectuer une recherche. Si le nom est pris, vous devriez entrer un autre nom dans la barre de recherche.
L'utilité d'un module ici, serait de nous aider à connaßtre la disponibilité des noms des modules sur npm. Plus besoin d'effectuer des recherches manuelles encore.


Un module oui, mais Ă  quel prix?

Développer un module est comme vous l'avez vu est assez bénéfique. Mais afin d'assurer une grande adoption (et un succÚs) de votre module, vous devez:

  • documenter assez bien votre module: le dĂ©crire et expliquer le fonctionnement de ce dernier;
  • Ă©crire des tests unitaires pour votre module;
  • communiquer autour, tout ce qui peut ĂȘtre utile pour assurer la popularitĂ© de votre module.

Trouver des emoji pertinents Ă  partir d'un texte 😼 ✹ 🙌 🐮 đŸ’„ 🙈


La librairie que nous allons construire est inspirée de emoj de sindresorhus. Le but de cette librairie est de trouver dans un texte, des émojis. En effet, nous utiliserons une API (une interface de programmation) qui utilise l'Apprentissage Profond aka Deep Learning afin de trouver une liste d'émojis cohérents correspondant à du texte. Ne vous laissez pas intimider par tous ces termes.

Développement dirigé par les tests (Test Driven Development - TDD en anglais)


L'approche TDD est une pratique trÚs utilisée dans le monde du développement informatique. Elle consiste à:

  • Ă©crire les tests unitaires en respectant les diffĂ©rentes fonctionnalitĂ©s de l'application;
  • Ă©crire ensuite le code source de votre application afin que tous les tests Ă©crits puissent 'passer'.

En résumé, pour faire du TDD, vous devez écrivez les tests de votre application et vous devez par la suite écrire le code source de votre application afin que tous les tests unitaires passent.

Un test unitaire, qu'est-ce que c'est?


Un test unitaire se charge de vĂ©rifier qu'un bout de code rĂ©alise une action prĂ©cise. Ainsi, nous pourrons Ă©crire un code vĂ©rifiant si la sortie de notre module est Ă©gale Ă  l'Ă©moji 'đŸ”„' si nous lui donnons comme texte fire. Plus votre code contient des fonctionnalitĂ©s, plus vous devez tester chacune d'entre elles.

Place Ă  la pratique


Mise en place de l'environnement de développement

$mkdir ts-emoj
$cd ts-emoj
$npm init -y
$npm install chai mocha ts-node @types/chai @types/mocha typescript --save-dev

Les instructions ci-dessus permettent de créer un dossier nommé ts-emoj, d'installer des modules Node.js pour notre environnement de développement. TypeScript est langage de programmation développé par Microsoft qui vise à fournir du typage au langage JavaScript. C'est la raison pour laquelle, nous installons les versions 'typées' des modules que nous installons (ts-node, @types/mocha et @types/chai).

Les tests unitaires

$touch test.spec.ts
Rajouter le code ci-dessous dans le fichier créé ci-dessus.
import { expect } from 'chai'; 1

describe('ts-emoj specs', () => { 2
    it('should return true', () => { 3
        expect(true).to.equal(true); 4
    });
});

Importer ou exporter un module, une fonction ou une classe avec TypeScript est assez intuitif. À la premiĂšre ligne 1, nous importons except de la librairie chai. Chai est un module d'assertion. Son rĂŽle est de vĂ©rifier la valeurs d'une ou plusieurs variables. La mĂ©thode except importĂ©e peut ĂȘtre interprĂ©tĂ©e comme 'vĂ©rifier'. À la ligne 4 nous vĂ©rifions que 'true' est Ă©gal Ă  'true'.

Le module Mocha que nous avons installĂ© est dĂ©fini comme un 'test runner'. Il permet d'exĂ©cuter les tests. À la ligne 3, la fonction describe('ts-emoji specs', () => {}) est appelĂ©e une suite ou collection de tests. Elle pourra contenir plusieurs fonctions dĂ©crivant diffĂ©rents tests: it('should return true', () => {}) 3

À prĂ©sent, modifions le fichier package.json. Nous devons indiquer Ă  Mocha les fichiers Ă  prendre en compte pour exĂ©cuter les diffĂ©rents tests unitaires.


"scripts": {
    "test": "mocha -r ts-node/register *.spec.ts"
}

À prĂ©sent, exĂ©cutons notre premier test unitaire.

$npm run test

Bravo, notre premier test passe. Modifions notre test afin qu'il puisse ĂȘtre conforme aux fontionnalitĂ©s de notre module. Nous devons nous assurer que notre module puisse retourner plusieurs au moins cet Ă©moji 🩄, lorsque l'on l'utilisateur lui fournit le mot 'unicorn'. Pour effectuer ce test, nous pouvons Ă©crire le code suivant comme test:

// On s'assure de récupérer les émojis (variable emojis) ici
expect(emojis).to.contain('🩄');

Comme nous écrivons un test unitaire, nous devons supposer que notre module a déjà été écrit et qu'il fonctionne à merveille. Ainsi, nous pourront dire que notre module sera défini dans une fonction emoj() dans un fichier index.ts.
Nous allons pouvoir rajouter au début de nos tests la ligne suivante:

import { emoj } from './index';

Comment fonctionne notre module?


Notre module fournit des Ă©mojis Ă  partir d'une sĂ©quence de texte. Il existe une API qui permet de rĂ©cupĂ©rer les dits Ă©mojis. Ainsi en accĂ©dant Ă  l'adresse suivant https://emoji.getdango.com/api/emoji?q=unicorn, vous obtenez une liste d'Ă©mojis liĂ©s au mot 'unicorn'. Nous venons d'effectuer une requĂȘte HTTP, et avons obtenu une rĂ©ponse aprĂšs un laps de temps. Nous devons reproduire le mĂȘme processus dans notre module.

Regardons l'Ă©tat de notre fichier test.spec.ts.

import { expect } from 'chai';
import { emoj } from './index';

describe('ts-emoji specs', () => {
    it('should return true', () => {        
        // On s'assure de récupérer les émojis (variable emojis) ici
        expect(emojis).to.contain('🩄');
    });
});

Récupérons à présent les émojis de notre module emoj toujours fictif. Rajoutons ainsi la ligne ci-dessous:

const emojis = emoj('unicorn');
Dans le code ci-dessus, nous rĂ©cupĂ©rons une liste d'Ă©mojis du module emoj. Mais, il faut noter que notre module doit faire des requĂȘtes HTTP. La durĂ©e de ces requĂȘtes n'Ă©tant pas connues, il est prudent d'utiliser async/await par exemple.
import { expect } from 'chai';
import { emoj } from './index';

describe('ts-emoji specs', () => { 1
    it('should return true', async () => {        
        const emojis = await emoj('unicorn'); 2
        expect(emojis).to.contain('🩄');
    });
});

À la ligne 2, nous introduisons le mot clĂ© await qui pourrait signifier: 'attendre la fin de l'exĂ©cution de cette instruction'. Puisque notre requĂȘte est asynchrone Ă  prĂ©sent, il faudra l'indiquer Ă  la mĂ©thode exĂ©cutant cette requĂȘte. 1

Notre test unitaire est à présent écrit. Et il respecte plus ou moins la fonctionalité de notre module. Si vous essayez de lancer le test avec npm run test, ce test échouera évidemment. Il va falloir écrire le code source de emoj afin que ce test puisse 'passer'.

Ecrivons le code source de emoj

$touch index.ts

Nous pouvons écrire notre fonction et l'exporter, donc la rendre accessible depuis l'extérieur.

export function emoj(params = '') {
}

Puis, nous devons trouver un moyen afin d'effectuer des requĂȘtes HTTP. Pour cela nous choisissons le module axios. Évidemment, vous avez le choix et vous pouver utiliser celui que vous prĂ©fĂ©rez.

$npm install axios @types/axios -D

import axios from 'axios';

export async function emoj(params = '') { 1
    try { 2
        const response = await axios.get('https://emoji.getdango.com/api/emoji', { 3
            params: {
                q: params
            }
        });
        return response.data.results.map((x: any) => x.text).join('  '); 5
    } catch (error) {
        return JSON.stringify(error); 6
    }
}        

Nous crĂ©ons donc la requĂȘte HTTP. Pour cela, nous Ă©crivons le requĂȘte 3: une requĂȘte de type GET, Ă  laquelle nous envoyons un paramĂštre. Ce paramĂštre n'est rien d'autre que la sĂ©quence de texte passĂ©e en argument, 'unicorn' dans nos tests par exemple. Nous utilisons l'instruction await afin de nous assurer d'avoir la rĂ©ponse dans la variable response dĂšs la fin de l'exĂ©cution de la requĂȘte. Etant donnĂ© que notre requĂȘte pourrait Ă©chouer, par exemple si le serveur distant est indisponible, nous pouvons mettre ces instructions dans une bloc try/catch, capable de dĂ©tecter une exception 1.
La ligne 6 se charge de retourner les erreurs Ă©ventuels (au format JSON) si la requĂȘte Ă©choue. On comprend alors aisĂ©ment pourquoi Ă  la ligne 1 nous avons indiquĂ© que la fonction emoj() est asynchrone.

Puis reste la ligne 5! Cette ligne se charge de retourner les donnĂ©es sous un format prĂ©cis. Lancez la requĂȘte suivante: https://emoji.getdango.com/api/emoji?q=unicorn. La rĂ©ponse retournĂ©e est un objet JSON possĂ©dant un attribut results. Cet attribut contient un tableau de valeurs: emojis et score. Nous devons extraire tous les Ă©mojis disponibles dans cette rĂ©ponse et les afficher Ă  l'utilisateur. C'est ce que fait la ligne 5.

Finalisons le module.

Lancez à présent les tests unitaires avec la commande npm run test Ils passent. Mais il se pourrait également qu'ils échouent. Pourquoi?
En effet, nous indiquons Ă  nos tests d'utiliser le module emoj que nous venons d'Ă©crire afin d'effectuer les tests. Le problĂšme est que notre module effectue une requĂȘte HTTP. Ces requĂȘtes peuvent Ă©chouer. Nos tests devraient ĂȘtre isolĂ©s. Pour cela, il existe une notion appelĂ©e mocking. Cette notion traduit juste le fait que vous pouvez controler les requĂȘtes qu'effectuent vos tests par exemple en fournissant une rĂ©ponse adĂ©quate.

Nous utilisons la librairie nock afin d'effectuer du HTTP mocking.
Reprenons le fichier des tests unitaires. Et rajoutons ceci:

import { expect } from 'chai';
import { emoj } from './index';
import nock from 'nock'; 1

describe('ts-emoji specs', () => {
    it('should return true', async () => {
        nock('https://emoji.getdango.com') 2
            .get('/api/emoji') 3
            .query({q: 'unicorn'}) 4
            .reply(200, { 5
                "results": [{
                    "text": "🩄",
                    "score": 0.095140554011
                }, {
                    "text": "🌈",
                    "score": 0.043754741549
                }, {
                    "text": "♄",
                    "score": 0.010567554273
                }, {
                    "text": "🐩",
                    "score": 0.009082570672
                }, {
                    "text": "🏰",
                    "score": 0.0075900820084
                }, {
                    "text": "❀",
                    "score": 0.0074062654749
                }, {
                    "text": "👑",
                    "score": 0.0071980352513
                }, {
                    "text": "🐉",
                    "score": 0.0065413345583
                }, {
                    "text": "✌",
                    "score": 0.0062650674954
                }, {
                    "text": "✹",
                    "score": 0.0061013558879
                }]
            })
        
        const emojis = await emoj('unicorn');
        expect(emojis).to.contain('🩄');
    });
});
    

Nous importons le module nock Ă  la ligne 1. Les lignes 2, 3 et 4 simulent la requĂȘte https://emoji.getdango.com/api/emoji?q=unicorn. À la ligne 5, nous retournons exactement la rĂ©ponse de la requĂȘte. Ainsi nos tests n'effectueront plus de requĂȘtes externes. Le mocking fournira la rĂ©ponse automatiquement.

Publier son module

Pour publier un sur npm rien de plus simple. Créez un compte sur le site, puis renseignez dans le fichier package.json le nom et la version de votre module.

$npm publish

Accédez à https://npmjs.com/package/ . Vous devriez voir une page pour votre nouveau module.


Bravo! Votre premier module est publié! Attendez-vous à un succÚs sans précédent. Le code source complet est disponible sur Github. Souscrivez à la newsletter de codedeck afin de recevoir les nouveaux tutoriels. Vous pouvez également rejoindre Slack pour poser toutes vos questions.

Partagez cet article

Partagez cet article !