Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Générer des PDF avec Node

Quand vient le temps de produire des documents ou des rapports à partir des données d’un système, plusieurs solutions s’offrent à nous. Dans des langages plus traditionnels en architecture monolithique (Roger : dans mon temps!), on avait l’habitude d’utiliser des générateurs de rapport comme LiveCycle ou JasperReport. Ce sont deux engins de rapport très complets, mais aussi assez compliqués à intégrer dans une application bâtie en Nodejs, en services REST. Aujourd’hui, on va voir comment y arriver 🙂

Configuration de l’environnement

Depuis un répertoire vide, initialiser un nouveau projet Node et installer la librairie PDFkit.

Puis, pour reproduire l’utilisation d’une API qui permet de télécharger le PDF, nous devrons installer Express.

Création de la route de téléchargement

Supposons que nous voulions une route pour télécharger un PDF. Je n’entrerai pas dans les détails d’Express parce que je l’ai couvert par le passé, mais nous devons créer le squelette de l’application, puis enfin créer la route. Créer un fichier index.js à côté du package.json :

Puis, on teste :

À cette adresse ( localhost:3000/api/TimeReport), vous devriez voir { ok: true }!

Encapsulation et façade

Viens donc le temps de mes recommandations! Lorsque vous utilisez une librairie tierce comme PDFkit ou n’importe quelle autre, vous devriez toujours encapsuler tout ce qui touche à cette librairie dans une ou plusieurs classes. Procéder de cette façon nous assure de pouvoir interchanger notre librairie dans le cas où nous nous buterions à une limitation de cette dernière.

Deuxième recommandation, lorsque vous utilisez une librairie aussi généraliste que PDFkit (ça génère un PDF, mais on a carte blanche), vous devriez au moins vous créer une façade (oui! le patron de conception). La façade sert à simplifier l’API d’une librairie tierce, par exemple. Créons d’abord une classe de service pour encapsuler le processus de génération de PDF :

Rien de sorcier jusqu’à maintenant, on ne fait que tester pour s’assurer que ça fonctionne bien. Modifions notre index.js pour que la route appelle ce service, en mode asynchrone :

Note : Ici, j’ai utilisé une Promise comme retour de ma classe de service parce que le processus de génération de PDF se fait via un stream asynchrone. L’utilisation de async/await est pour simplifier la syntaxe.

Si vous testez, vous devriez obtenir le même comportement.

Maintenant, créons la façade. Pour générer notre TimeReport, nous aurons besoin de :

  1. Créer le document PDF et son stream vers le système de fichiers
  2. Ajouter un titre au centre
  3. Ajouter un libellé avec la date et l’heure actuelle
  4. Fermer le document PDF et obtenir les octets

C’est donc cet algorithme simplifié qui sera notre façade :

createDocument()

Cette fonction permet de créer un nouveau document PDF et d’écrire le résultat sur le disque. Remarquez l’utilisation de return this pour permettre le chaînage d’appels.

writeTitle(title)

Celle-ci est une méthode propre de la façade. Puisqu’une façade sert à simplifier une API, on peut par exemple déterminer que tous nos titres seront en gras et centré sur la page. Cette méthode sert donc à appeler les différentes méthode de l’API de PDFkit afin que ce qui soit propager dans notre code soit uniquement writeTitle() (par exemple).

writeLabelValuePair(label, value)

Même principe que pour la fonction précédente. C’était pour montrer comment on peut avoir plusieurs méthodes dans une même façade.

closeAndGetBytes()

Cette fonction ferme le flux et écrit sur le disque. Notez l’utilisation d’un setTimeout(). Ce que j’ai pu remarqué avec mes tests de la librairie, c’est que la fonction end() qui initie l’écriture sur le disque retourne void et non une Promise comme on pourrait s’y attendre. C’est donc impossible (à moins qu’un bon samaritain me pointe la solution en commentaire) d’attendre que le flux soit fermé et le fichier créé avant de poursuivre.

Comme je dis trop souvent : c’est un gros plaster avec du poil. Ou encore (pour nos amis européens) un gros diachylon velu 🙂

Retourner les données en PDF

Concernant la couche de service qui appelle la façade, son algo sera très simple :

On sait donc que closeAndGetBytes() nous retourne une Promise qui sera résolue 250 ms après la fermeture du flux PDF. Pour retourner le PDF en téléchargement, simplement :

Maintenant, si vous atteignez l’URL, vous devriez voir ce PDF :

Conclusion

Aujourd’hui, vous avez appris un cas d’utilisation réel d’une façade pour encapsuler et simplifier l’API d’une librairie tierce. Les avantages de procéder de cette façon sont : une diminution du couplage puisque la librairie est encapsulée, PDFkit se limite à un seul fichier source facilement remplaçable, une augmentation de cohésion dans le sens où notre classe de logique d’affaires ne génère pas de PDF à proprement parler, c’est le rôle de la façade, et un meilleur respect du principe de responsabilité unique.

Pour poursuivre votre apprentissage ou la découverte de PDFkit, je vous invite à lire la documentation très complète de la libraire. N’oubliez pas de partager cet article 🙂 Ça ne prend qu’une seconde et ça nous aide beaucoup à nous faire connaître!

Cheers :-*

Suivez-nous par courriel!

Saisissez votre adresse courriel pour vous abonner au blog d'Ezo et recevoir une notification de chaque nouvel article par email.