error
et comment l'utiliser pour une manière plus efficace et plus efficace de gérer les erreurs dans vos applications.
La gestion des erreurs est l'une de ces parties du développement logiciel qui ne sont pas tout à fait obtenir toute l’attention qu’elle mérite. Cependant, la création d'applications robustes nécessite de traiter correctement les erreurs.
Vous pouvez vous débrouiller dans NodeJS sans gérer correctement les erreurs, mais en raison de la nature asynchrone de NodeJS, une mauvaise manipulation ou des erreurs peuvent vous causer des problèmes assez rapidement, en particulier lors du débogage des applications. [19659006] Avant de continuer, j'aimerais souligner le type d'erreurs dont nous allons discuter comment utiliser les classes d'erreurs.
Erreurs opérationnelles
Ce sont des erreurs découvertes pendant l'exécution d'un programme. Les erreurs opérationnelles ne sont pas des bogues et peuvent se produire de temps en temps principalement en raison d'un ou d'une combinaison de plusieurs facteurs externes comme l'expiration du délai d'un serveur de base de données ou la décision d'un utilisateur de tenter une injection SQL en entrant des requêtes SQL dans un champ de saisie. [19659006] Voici d'autres exemples d'erreurs opérationnelles:
- Échec de la connexion à un serveur de base de données;
- Entrées non valides par l'utilisateur (le serveur répond avec un code de réponse
400
); - Délai d'expiration de la demande ;
- Ressource introuvable (le serveur répond avec un code de réponse 404);
- Le serveur retourne avec une réponse
500
.
Il est également intéressant de noter de discuter brièvement de la contrepartie des erreurs opérationnelles.
Erreurs de programmation
Ce sont des bogues dans le programme qui peuvent être résolus en changeant le code. Ces types d'erreurs ne peuvent pas être traités car ils se produisent à la suite de la rupture du code. Voici des exemples d'erreurs:
- Tentative de lecture d'une propriété sur un objet non défini.
const user = {
prénom: 'Kelvin',
nom de famille: 'Omereshone',
}
console.log (user.fullName) // renvoie 'undefined' car la propriété fullName n'est pas définie
- Invocation ou appel d'une fonction asynchrone sans rappel.
- Transmission d'une chaîne là où un nombre était attendu.
Ceci L'article porte sur Traitement des erreurs opérationnelles dans NodeJS. La gestion des erreurs dans NodeJS est très différente de la gestion des erreurs dans d'autres langues. Cela est dû à la nature asynchrone de JavaScript et à l'ouverture de JavaScript aux erreurs. Laissez-moi vous expliquer:
En JavaScript, les instances de la classe error
ne sont pas la seule chose que vous pouvez lancer. Vous pouvez littéralement lancer n'importe quel type de données cette ouverture n'est pas autorisée par d'autres langages.
Par exemple, un développeur JavaScript peut décider d'ajouter un nombre au lieu d'une instance d'objet d'erreur, comme ceci:
// bad
lancer 'Oups :)';
// bien
lancer une nouvelle erreur ('Whoops :)')
Vous ne verrez peut-être pas le problème en jetant d'autres types de données, mais cela entraînera un débogage plus difficile car vous n'obtiendrez pas de trace de pile et d'autres propriétés que l'objet Error expose qui sont nécessaire pour le débogage.
Examinons quelques modèles incorrects dans la gestion des erreurs, avant de jeter un coup d'œil au modèle de classe Error et en quoi c'est un bien meilleur moyen de gérer les erreurs dans NodeJS.
Bad Error Handling Pattern # 1: Mauvaise utilisation des rappels
Scénario du monde réel : Votre code dépend d'une API externe nécessitant un rappel pour obtenir le résultat que vous attendez de lui.
Prenons le ci-dessous l'extrait de code:
'use strict';
const fs = require ('fs');
const write = function () {
fs.mkdir ('./ writeFolder');
fs.writeFile ('./ writeFolder / foobar.txt', 'Hello World');
}
écrire();
Jusqu'à NodeJS 8 et au-dessus, le code ci-dessus était légitime et les développeurs se contentaient de lancer et d'oublier les commandes. Cela signifie que les développeurs n'étaient pas obligés de fournir un rappel à ces appels de fonction et pouvaient donc omettre la gestion des erreurs. Que se passe-t-il lorsque le writeFolder
n’a pas été créé? L'appel à writeFile
ne sera pas effectué et nous n'en saurons rien. Cela peut également entraîner une condition de concurrence car la première commande peut ne pas s'être terminée lorsque la deuxième commande a redémarré, vous ne le sauriez pas.
Commençons par résoudre ce problème en résolvant la condition de concurrence. Nous le ferions en donnant un rappel à la première commande mkdir
pour s'assurer que le répertoire existe bien avant d'y écrire avec la deuxième commande. Donc notre code ressemblerait à celui ci-dessous:
'use strict';
const fs = require ('fs');
const write = function () {
fs.mkdir ('./ writeFolder', () => {
fs.writeFile ('./ writeFolder / foobar.txt', 'Hello World!');
});
}
écrire();
Bien que nous ayons résolu la condition de race, nous n'avons pas encore tout à fait terminé. Notre code pose toujours problème car même si nous avons utilisé un callback pour la première commande, nous n'avons aucun moyen de savoir si le dossier writeFolder
a été créé ou non. Si le dossier n’a pas été créé, le deuxième appel échouera à nouveau, mais nous avons encore ignoré l’erreur. Nous résolvons cela en…
Gestion des erreurs avec les rappels
Afin de gérer correctement les erreurs avec les rappels, vous devez vous assurer de toujours utiliser l'approche «erreur d'abord». Cela signifie que vous devez d'abord vérifier s'il y a une erreur renvoyée par la fonction avant de continuer à utiliser les données (le cas échéant) qui ont été renvoyées. Voyons la mauvaise façon de procéder:
'use strict';
// Faux
const fs = require ('fs');
const write = function (rappel) {
fs.mkdir ('./ writeFolder', (err, data) => {
if (data) fs.writeFile ('./ writeFolder / foobar.txt', 'Hello World!');
else callback (err)
});
}
écrire (console.log);
Le modèle ci-dessus est incorrect car l'API que vous appelez peut parfois ne pas renvoyer de valeur ou renvoyer une valeur erronée comme valeur de retour valide. Cela vous ferait finir dans un cas d'erreur même si vous pourriez apparemment avoir un appel réussi de la fonction ou de l'API.
Le modèle ci-dessus est également mauvais parce que son utilisation mangerait votre erreur (vos erreurs ne seront pas appelées même si cela aurait pu arriver). Vous n'aurez également aucune idée de ce qui se passe dans votre code suite à ce type de modèle de gestion des erreurs. Donc, la bonne façon pour le code ci-dessus serait:
'use strict';
// Droite
const fs = require ('fs');
const write = function (rappel) {
fs.mkdir ('./ writeFolder', (err, data) => {
if (err) return callback (err)
fs.writeFile ('./ writeFolder / foobar.txt', 'Hello World!');
});
}
écrire (console.log);
Mauvais schéma de gestion des erreurs n ° 2: mauvaise utilisation des promesses
Scénario du monde réel : Vous avez donc découvert les promesses et vous pensez qu'elles sont bien meilleures que les rappels à cause de l'enfer des rappels et vous a décidé de promettre une API externe dont dépendait votre base de code. Ou vous consommez une promesse d'une API externe ou d'une API de navigateur comme la fonction fetch ().
De nos jours, nous n'utilisons pas vraiment de rappels dans nos bases de code NodeJS, nous utilisons promesses . Réimplémentons donc notre exemple de code avec une promesse:
'use strict';
const fs = require ('fs'). promesses;
const write = function () {
retourne fs.mkdir ('./ writeFolder'). then (() => {
fs.writeFile ('./ writeFolder / foobar.txt', 'Bonjour tout le monde!')
}). catch ((err) => {
// attraper toutes les erreurs potentielles
console.error (err)
})
}
Mettons le code ci-dessus sous un microscope – nous pouvons voir que nous bifurquons la promesse fs.mkdir
dans une autre chaîne de promesse (l'appel à fs.writeFile) sans même traiter cet appel de promesse. Vous pourriez penser qu'une meilleure façon de le faire serait:
'use strict';
const fs = require ('fs'). promesses;
const write = function () {
retourne fs.mkdir ('./ writeFolder'). then (() => {
fs.writeFile ('./ writeFolder / foobar.txt', 'Hello world!'). then (() => {
// faire quelque chose
}). catch ((err) => {
console.error (err);
})
}). catch ((err) => {
// attraper toutes les erreurs potentielles
console.error (err)
})
}
Mais ce qui précède ne serait pas mis à l'échelle. C'est parce que si nous avons plus de chaîne de promesses à appeler, nous nous retrouverons avec quelque chose de similaire à l'enfer de rappel que les promesses ont été faites pour résoudre. Cela signifie que notre code continuera d'être indenté vers la droite. Nous aurions une promesse d'enfer entre nos mains.
Promettre une API basée sur le rappel
La plupart du temps, vous voudriez promettre une API basée sur le rappel par vous-même afin de mieux gérer les erreurs sur cette API. Cependant, ce n'est pas vraiment facile à faire. Prenons un exemple ci-dessous pour expliquer pourquoi.
function doesWillNotAlwaysSettle (arg) {
retourner une nouvelle promesse ((résoudre, rejeter) => {
doATask (foo, (err) => {
if (err) {
retourner rejeter (err);
}
si (arg === vrai) {
résoudre ('I am Done')
}
});
});
}
D'après ce qui précède, si arg
n'est pas true
et que nous n'avons pas d'erreur de l'appel à la fonction doATask
alors cette promesse sera juste hang out qui est une fuite de mémoire dans votre application.
Erreurs de synchronisation avalées dans les promesses
L'utilisation du constructeur Promise présente plusieurs difficultés, l'une de ces difficultés est; dès qu'il est résolu ou rejeté, il ne peut pas obtenir un autre état. En effet, une promesse ne peut obtenir qu'un seul état – soit elle est en attente, soit elle est résolue / rejetée. Cela signifie que nous pouvons avoir des zones mortes dans nos promesses. Voyons ceci dans le code:
function deadZonePromise (arg) {
retourner une nouvelle promesse ((résoudre, rejeter) => {
doATask (foo, (err) => {
résoudre («je suis tout fait»);
throw new Error ('Je ne suis jamais atteint') // Dead Zone
});
});
}
D'après ce qui précède, nous voyons que dès que la promesse est résolue, la ligne suivante est une zone morte et ne sera jamais atteinte. Cela signifie que toute gestion d'erreur synchrone suivante exécutée dans vos promesses sera simplement avalée et ne sera jamais lancée.
Exemples du monde réel
Les exemples ci-dessus aident à expliquer les mauvais modèles de gestion des erreurs, jetons un coup d'œil au type de problèmes vous pourriez voir dans la vie réelle.
Exemple du monde réel # 1 – Transformer une erreur en chaîne
Scénario : Vous avez décidé que l'erreur renvoyée par une API n'est pas vraiment suffisante pour vous vous avez donc décidé d'y ajouter votre propre message.
'use strict';
function readTemplate () {
retourne une nouvelle promesse (() => {
databaseGet ('requête', fonction (err, données) {
if (err) {
rejeter ("Modèle introuvable. Erreur:", + err);
} autre {
résoudre (données);
}
});
});
}
readTemplate ();
Voyons ce qui ne va pas avec le code ci-dessus. D'après ce qui précède, nous voyons que le développeur tente d'améliorer l'erreur générée par l'API databaseGet
en concaténant l'erreur renvoyée avec la chaîne «Template not found». Cette approche présente de nombreux inconvénients car lorsque la concaténation a été effectuée, le développeur exécute implicitement toString
sur l'objet d'erreur renvoyé. De cette façon, il perd toutes les informations supplémentaires renvoyées par l'erreur (dites adieu à la trace de pile). Donc, ce que le développeur a en ce moment est juste une chaîne qui n'est pas utile lors du débogage.
Une meilleure façon est de garder l'erreur telle qu'elle est ou de l'envelopper dans une autre erreur que vous avez créée et jointe l'erreur renvoyée par le databaseGet call en tant que propriété.
Exemple concret n ° 2: Ignorer complètement l'erreur
Scénario : Peut-être lorsqu'un utilisateur s'inscrit dans votre application, en cas d'erreur vous voulez juste attraper l'erreur et afficher un message personnalisé mais vous avez complètement ignoré l'erreur qui a été interceptée sans même la consigner à des fins de débogage.
router.get ('/: id', function (req, res, suivant) {
database.getData (req.params.userId)
.then (fonction (données) {
if (data.length) {
res.status (200) .json (données);
} autre {
res.status (404) .end ();
}
})
.catch (() => {
log.error ('db.rest/get: n'a pas pu obtenir les données:', req.params.userId);
res.status (500) .json ({error: 'Erreur interne du serveur'});
})
});
D'après ce qui précède, nous pouvons voir que l'erreur est complètement ignorée et que le code envoie 500 à l'utilisateur si l'appel à la base de données a échoué. Mais en réalité, la cause de l'échec de la base de données pourrait être des données malformées envoyées par l'utilisateur, ce qui est une erreur avec le code d'état 400.
Dans le cas ci-dessus, nous finirions par une horreur de débogage parce que vous en tant que le développeur ne saurait pas ce qui ne va pas. L'utilisateur ne sera pas en mesure de fournir un rapport correct, car 500 erreurs de serveur interne sont toujours générées. Vous finiriez par perdre des heures à trouver le problème qui équivaudrait à un gaspillage de temps et d'argent de votre employeur.
Exemple réel n ° 3: ne pas accepter l'erreur générée par une API
Scénario : Une erreur a été générée par une API que vous utilisiez, mais vous n'acceptez pas cette erreur à la place, vous la gérez et la transformez de manière à la rendre inutile à des fins de débogage.
Prenons l'exemple de code suivant ci-dessous:
fonction asynchrone doThings (entrée) {
essayez {
valider (entrée);
essayez {
attendre db.create (entrée);
} catch (erreur) {
error.message = `Erreur interne: $ {error.message}`
if (instance d'erreur de Klass) {
error.isKlass = true;
}
erreur de jet
}
} catch (erreur) {
error.message = `Impossible de faire les choses: $ {error.message}`;
attendre le retour en arrière (entrée);
lancer une erreur;
}
}
Il se passe beaucoup de choses dans le code ci-dessus qui conduiraient à l'horreur du débogage. Jetons un coup d’œil:
- Wrapping
try / catch
blocks: Vous pouvez voir ci-dessus que nous envelopponstry / catch
block, ce qui est une très mauvaise idée. Nous essayons normalement de réduire l'utilisation des blocstry / catch
pour minimiser la surface où nous aurions à gérer notre erreur (pensez-y comme traitement d'erreur DRY); - Nous manipulons également le message d'erreur dans la tentative d'amélioration qui n'est pas non plus une bonne idée;
- Nous vérifions si l'erreur est une instance de type
Klass
et dans ce cas, nous définissons une propriété booléenne de l'erreur] isKlass
à truev (mais si ce contrôle réussit, l'erreur est du typeKlass
); - Nous annulons également la base de données trop tôt car, à partir de la structure du code, il y a une tendance élevée que nous n'avons peut-être même pas atteint la base de données lorsque l'erreur a été générée.
Voici une meilleure façon d'écrire le code ci-dessus:
fonction async doThings (entrée) {
valider (entrée);
essayez {
attendre db.create (entrée);
} catch (erreur) {
essayez {
attendre rollback ();
} catch (erreur) {
logger.log ('Rollback failed', erreur, 'input:', input);
}
lancer une erreur;
}
}
Analysons ce que nous faisons correctement dans l'extrait ci-dessus:
- Nous utilisons un bloc
try / catch
et ce n'est que dans le bloc catch que nous utilisons un autretry / catch
] qui doit servir de garde au cas où quelque chose se passe avec cette fonction d'annulation et que nous enregistrons cela; - Enfin, nous lançons notre erreur reçue d'origine, ce qui signifie que nous ne perdons pas le message inclus dans cette erreur. [19659084] Test
Nous souhaitons surtout tester notre code (manuellement ou automatiquement). Mais la plupart du temps, nous ne testons que les choses positives. Pour un test robuste, vous devez également tester les erreurs et les cas extrêmes. Cette négligence est responsable de la découverte de bogues dans la production, ce qui coûterait plus de temps de débogage supplémentaire.
Astuce : Assurez-vous toujours de tester non seulement les choses positives (obtenir un code d'état de 200 à partir d'un point final) mais aussi tous les cas d'erreur et tous les cas extrêmes.
Exemple réel n ° 4: Rejections non gérées
Si vous avez déjà utilisé des promesses, vous avez probablement rencontré
non géré rejets
.Voici une brève introduction sur les rejets non gérés. Les refus non traités sont des refus de promesse qui n'ont pas été traités. Cela signifie que la promesse a été rejetée mais que votre code continuera de fonctionner.
Regardons un exemple courant dans le monde réel qui conduit à des rejets non gérés.
'use strict'; fonction asynchrone foobar () { lancer une nouvelle erreur ('foobar'); } fonction asynchrone baz () { lancer une nouvelle erreur ('baz') } (fonction asynchrone doThings () { const a = foobar (); const b = baz (); essayez { attendre un; attendre b; } catch (erreur) { // ignore toutes les erreurs! } }) ();
Le code ci-dessus à première vue peut ne pas sembler sujet aux erreurs. Mais en y regardant de plus près, nous commençons à voir un défaut. Laissez-moi vous expliquer: que se passe-t-il lorsque
un
est rejeté? Cela signifie queattendre b
n'est jamais atteint et cela signifie que c'est un rejet non géré. Une solution possible est d'utiliserPromise.all
sur les deux promesses. Ainsi, le code se lirait comme suit:'use strict'; fonction asynchrone foobar () { lancer une nouvelle erreur ('foobar'); } fonction asynchrone baz () { lancer une nouvelle erreur ('baz') } (fonction asynchrone doThings () { const a = foobar (); const b = baz (); essayez { attendre Promise.all ([a, b]); } catch (erreur) { // ignore toutes les erreurs! } }) ();
Voici un autre scénario réel qui conduirait à une erreur de rejet de promesse non gérée:
'use strict'; fonction asynchrone foobar () { lancer une nouvelle erreur ('foobar'); } fonction asynchrone doThings () { essayez { retour foobar () } capture { // ignorer à nouveau les erreurs! } } faire des choses();
Si vous exécutez l'extrait de code ci-dessus, vous obtiendrez un rejet de promesse non géré, et voici pourquoi: Bien que ce ne soit pas évident, nous retournons une promesse (foobar) avant de la traiter avec le
try / catch
. Ce que nous devrions faire, c'est attendre la promesse que nous gérons avec letry / catch
pour que le code se lise:'use strict'; fonction asynchrone foobar () { lancer une nouvelle erreur ('foobar'); } fonction asynchrone doThings () { essayez { retour attendre foobar () } capture { // ignorer à nouveau les erreurs! } } faire des choses();
Conclusion sur les choses négatives
Maintenant que vous avez vu de mauvais modèles de gestion des erreurs et des correctifs possibles, examinons maintenant le modèle de classe Error et comment il résout le problème de la mauvaise gestion des erreurs dans NodeJS.
Error Classes
Dans ce modèle, nous commencerions notre application avec une classe
ApplicationError
de cette façon, nous savons que toutes les erreurs dans nos applications que nous lançons explicitement vont en hériter. Nous commencerions donc par les classes d'erreur suivantes:-
ApplicationError
Il s'agit de l'ancêtre de toutes les autres classes d'erreur, c'est-à-dire que toutes les autres classes d'erreur en héritent. -
DatabaseError
Any erreur relative aux opérations de la base de données héritera de cette classe. -
UserFacingError
Toute erreur produite à la suite de l'interaction d'un utilisateur avec l'application serait héritée de cette classe.
Voici comment notre
] erreur
le fichier de classe ressemblerait à:'use strict'; // Voici les classes d'erreur de base à étendre La classe ApplicationError étend l'erreur { obtenir le nom () { retourne this.constructor.name; } } La classe DatabaseError étend ApplicationError {} La classe UserFacingError étend ApplicationError {} module.exports = { Erreur d'application, Erreur de la base de données, UserFacingError }
Cette approche nous permet de distinguer les erreurs générées par notre application. Donc maintenant, si nous voulons gérer une erreur de demande incorrecte (entrée utilisateur invalide) ou une erreur non trouvée (ressource non trouvée), nous pouvons hériter de la classe de base qui est
UserFacingError
(comme dans le code ci-dessous).const {UserFacingError} = require ('./ baseErrors') La classe BadRequestError étend UserFacingError { constructeur (message, options = {}) { super (message); // Vous pouvez joindre des informations pertinentes à l'instance d'erreur // (par exemple, le nom d'utilisateur) for (const [key, value] of Object.entries (options)) { this [key] = valeur; } } get statusCode () { return 400; } } La classe NotFoundError étend UserFacingError { constructeur (message, options = {}) { super (message); // Vous pouvez joindre des informations pertinentes à l'instance d'erreur // (par exemple, le nom d'utilisateur) for (const [key, value] of Object.entries (options)) { this [key] = valeur; } } get statusCode () { retour 404 } } module.exports = { BadRequestError, Erreur non trouvée }
L'un des avantages de l'approche de classe
error
est que si nous lançons l'une de ces erreurs, par exemple, uneNotFoundError
chaque développeur lisant cette base de code serait en mesure de comprendre ce qui se passe à ce moment-là (s’ils lisent le code).Vous pourriez également transmettre plusieurs propriétés spécifiques à chaque classe d’erreur lors de l’instanciation de cette erreur.
Un autre avantage clé est que vous pouvez avoir des propriétés qui font toujours partie d'une classe d'erreur, par exemple, si vous recevez une erreur UserFacing, vous savez qu'un statusCode fait toujours partie de cette classe d'erreur maintenant vous pouvez simplement l'utiliser directement dans le code plus tard.
Conseils sur l'utilisation des classes d'erreur
- Créez votre propre module (éventuellement privé) pour chaque classe d'erreur de cette façon, vous pouvez simplement l'importer dans votre application et l'utiliser partout.
- Ne renvoyez que les erreurs qui vous intéressent (erreurs qui sont des instances de vos classes d'erreur). De cette façon, vous savez que vos classes d'erreur sont votre seule source de vérité et qu'elles contiennent toutes les informations nécessaires pour déboguer votre application.
- Avoir un module d'erreur abstrait est très utile car nous connaissons maintenant toutes les informations nécessaires concernant les erreurs que nos applications peuvent générer. un endroit.
- Gérez les erreurs dans les couches. Si vous gérez des erreurs partout, vous avez une approche incohérente de la gestion des erreurs qui est difficile à suivre. Par couches, j'entends comme base de données, couches express / fastify / HTTP, etc.
Voyons à quoi ressemblent les classes d'erreur dans le code. Voici un exemple en express:
const {DatabaseError} = require ('./ error') const {NotFoundError} = require ('./ userFacingErrors') const {UserFacingError} = require ('./ error') // Express app.get ('/: id', fonction asynchrone (req, res, next) { laisser les données essayez { data = attendre database.getData (req.params.userId) } catch (err) { retourne suivant (err); } if (! data.length) { return next (new NotFoundError ('Dataset not found')); } res.status (200) .json (données) }) app.use (function (err, req, res, next) { if (err instanceof UserFacingError) { res.sendStatus (err.statusCode); // ou res.status (err.statusCode) .send (err.errorCode) } autre { res.sendStatus (500) } // fais ta logique logger.error (err, 'Paramètres:', req.params, 'Données utilisateur:', req.user) });
De ce qui précède, nous tirons parti du fait qu'Express expose un gestionnaire d'erreurs global qui vous permet de gérer toutes vos erreurs en un seul endroit. Vous pouvez voir l'appel à
next ()
aux endroits où nous traitons les erreurs. Cet appel transmettrait les erreurs au gestionnaire qui est défini dans la sectionapp.use
. Parce qu'express ne prend pas en charge async / await, nous utilisons les blocstry / catch
.Donc, à partir du code ci-dessus, pour gérer nos erreurs, nous devons simplement vérifier si l'erreur qui a été générée est une
] UserFacingError
et automatiquement nous savons qu'il y aurait un statusCode dans l'objet d'erreur et nous l'envoyons à l'utilisateur (vous voudrez peut-être aussi avoir un code d'erreur spécifique que vous pouvez transmettre au client) et c'estVous remarquerez également que dans ce modèle (
error
class pattern) toute autre erreur que vous n'avez pas explicitement renvoyée est une erreur500
car c'est quelque chose d'inattendu cela signifie que vous n'avez pas explicitement renvoyé cette erreur dans votre application. De cette façon, nous pouvons distinguer les types d'erreurs qui se produisent dans nos applications.Conclusion
Une gestion correcte des erreurs dans votre application peut vous permettre de mieux dormir la nuit et gagner du temps de débogage. Voici quelques points clés à retenir de cet article:
- Utilisez les classes d'erreur spécifiquement configurées pour votre application;
- Implémentez des gestionnaires d'erreurs abstraits;
- Utilisez toujours async / await;
- Rendez les erreurs expressives; [19659012] L'utilisateur promet si nécessaire;
- Renvoie les statuts et codes d'erreur appropriés;
- Utilisez les hooks de promesse.
(ra, yk, il) -
Source link