Création d'une API REST avec Node.js et une base de données Oracle. Partie 4

Partie 4. Création d'une API REST: gestion des requêtes POST, PUT et DELETE

Dans l' article précédent, vous avez ajouté une logique à l'API pour les demandes GET qui ont récupéré des données d'une base de données. Dans cet article, vous allez compléter les fonctionnalités de base de l'API CRUD en ajoutant de la logique pour gérer les requêtes POST, PUT et DELETE.

Ajout d'une logique de routage

Pour simplifier la logique de routage, vous redirigerez toutes les méthodes HTTP via une route existante (avec un paramètre id facultatif). Ouvrez le fichier services / router.js et remplacez la logique de routage actuelle (lignes 5-6) par le code suivant:

router.route('/employees/:id?') .get(employees.get) .post(employees.post) .put(employees.put) .delete(employees.delete); 

La logique de routage mise à jour mappe les quatre méthodes HTTP les plus courantes utilisées pour les opérations CRUD de base avec la logique de contrôleur correcte.

Traitement des requêtes POST

Les requêtes HTTP POST sont utilisées pour créer de nouvelles ressources (dans ce cas, les enregistrements des employés). L'idée principale est d'extraire des données du corps de la requête HTTP et de les utiliser pour créer une nouvelle ligne dans la base de données. Pour ajouter une logique de contrôleur pour les demandes POST, ouvrez le fichier controllers / employee.js et ajoutez le code suivant:

 function getEmployeeFromRec(req) { const employee = { first_name: req.body.first_name, last_name: req.body.last_name, email: req.body.email, phone_number: req.body.phone_number, hire_date: req.body.hire_date, job_id: req.body.job_id, salary: req.body.salary, commission_pct: req.body.commission_pct, manager_id: req.body.manager_id, department_id: req.body.department_id }; return employee; } async function post(req, res, next) { try { let employee = getEmployeeFromRec(req); employee = await employees.create(employee); res.status(201).json(employee); } catch (err) { next(err); } } module.exports.post = post; 

La fonction getEmployeeFromRec prend un objet de demande et retourne un objet avec les propriétés nécessaires pour créer un enregistrement d'employé. La fonction a été déclarée en dehors de la fonction post afin de pouvoir être utilisée ultérieurement pour les requêtes PUT.

La fonction post utilise getEmployeeFromRec pour initialiser la variable, qui est ensuite transmise à la méthode create de l'API de base de données des employés. Après l'opération de création, le code d'état «201 créé» est envoyé au client avec le JSON de l'employé (y compris la nouvelle valeur de l'identifiant de l'employé).

Vous pouvez maintenant faire attention à la logique de l'API de base de données. Ouvrez le fichier db_apis / employee.js et ajoutez le code suivant ci-dessous.

 const createSql = `insert into employees ( first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id ) values ( :first_name, :last_name, :email, :phone_number, :hire_date, :job_id, :salary, :commission_pct, :manager_id, :department_id ) returning employee_id into :employee_id`; async function create(emp) { const employee = Object.assign({}, emp); employee.employee_id = { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } const result = await database.simpleExecute(createSql, employee); employee.employee_id = result.outBinds.employee_id[0]; return employee; } module.exports.create = create; 

La logique ci-dessus commence par déclarer une constante nommée createSql pour stocker l'instruction d'insertion. Notez qu'il utilise des variables de liaison , pas une concaténation de chaînes, pour faire référence aux valeurs à insérer. Il convient de répéter l'importance des variables de liaison pour des raisons de sécurité et de performances. Essayez d'éviter la concaténation de chaînes autant que possible.

Dans la fonction de création, la constante employé est définie et initialisée pour une copie du paramètre emp à l'aide d'Object.assign. Cela empêche la modification directe de l'objet transféré depuis le contrôleur.

Ensuite, la propriété employee_id est ajoutée à l'objet employee (configuré comme «out bind») afin qu'il contienne toutes les variables de liaison nécessaires pour exécuter l'instruction SQL. Ensuite, la fonction simpleExecute est utilisée pour exécuter l'instruction insert et la propriété outBinds est utilisée pour remplacer la propriété employee.employee_id avant de renvoyer l'objet.

Puisqu'il existe un lien vers le module oracledb, vous devez ajouter la ligne suivante en haut du fichier.
 const oracledb = require('oracledb'); 


Traitement des demandes PUT

Les requêtes PUT seront utilisées pour mettre à jour les ressources existantes. Il est important de noter que PUT est utilisé pour remplacer la ressource entière - il n'effectue pas de mises à jour partielles (je vais vous montrer comment faire cela avec PATCH à l'avenir). Revenez au fichier controllers / employee.js et ajoutez le code suivant ci-dessous.

 async function put(req, res, next) { try { let employee = getEmployeeFromRec(req); employee.employee_id = parseInt(req.params.id, 10); employee = await employees.update(employee); if (employee !== null) { res.status(200).json(employee); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.put = put; 

La fonction put utilise getEmployeeFromRec pour initialiser un objet nommé employee, puis ajoute la propriété employee_id du paramètre id à l'URL. Ensuite, l'objet employé est transmis à la fonction de mise à jour de l'API de base de données et la réponse correspondante est envoyée au client en fonction du résultat.

Pour ajouter la logique de mise à jour à l'API de base de données, ajoutez le code suivant au fichier db_apis / employee.js .

 const updateSql = `update employees set first_name = :first_name, last_name = :last_name, email = :email, phone_number = :phone_number, hire_date = :hire_date, job_id = :job_id, salary = :salary, commission_pct = :commission_pct, manager_id = :manager_id, department_id = :department_id where employee_id = :employee_id`; async function update(emp) { const employee = Object.assign({}, emp); const result = await database.simpleExecute(updateSql, employee); if (result.rowsAffected && result.rowsAffected === 1) { return employee; } else { return null; } } module.exports.update = update; 

La logique de mise à jour est très similaire à la logique de création. Une variable est déclarée pour stocker l'instruction SQL, puis simpleExecute est utilisé pour exécuter l'instruction avec les valeurs dynamiques transférées (après les avoir copiées vers un autre objet). result.rowsAffected est utilisé pour déterminer si la mise à jour a réussi et renvoyer la valeur correcte.

SUPPRIMER le traitement des demandes

La dernière méthode que vous devez implémenter est DELETE, qui, sans surprise, supprimera les ressources de la base de données. Ajoutez le code suivant à la fin du fichier controllers / employee.js.

 async function del(req, res, next) { try { const id = parseInt(req.params.id, 10); const success = await employees.delete(id); if (success) { res.status(204).end(); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.delete = del; 

Le moteur JavaScript lèvera une exception si vous essayez de déclarer une fonction appelée «supprimer» à l'aide de l'opérateur de fonction. Pour contourner ce problème, une fonction nommée «del» est déclarée, puis exportée en tant que «supprimer».

À ce stade, vous devriez être capable de lire et de comprendre la logique. Contrairement aux exemples précédents, cette requête HTTP n'a pas de corps; seul le paramètre id est utilisé dans le chemin de l'itinéraire. Le code d'état «204 Aucun contenu» est souvent utilisé avec les demandes DELETE lorsque le corps de réponse n'est pas envoyé.

Pour terminer la logique de la base de données, revenez au fichier db_apis / employee.js et ajoutez le code suivant à la fin.

 const deleteSql = `begin delete from job_history where employee_id = :employee_id; delete from employees where employee_id = :employee_id; :rowcount := sql%rowcount; end;` async function del(id) { const binds = { employee_id: id, rowcount: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } } const result = await database.simpleExecute(deleteSql, binds); return result.outBinds.rowcount === 1; } module.exports.delete = del; 

Étant donné que la table JOB_HISTORY a une contrainte de clé étrangère qui fait référence à la table EMPLOYEES, un simple bloc PL / SQL est utilisé pour supprimer les lignes nécessaires des deux tables en un seul cycle.

Analyse des requêtes JSON

Si vous regardez la fonction getEmployeeFromRec dans controllers / employee.js, vous remarquerez que la propriété du corps de la demande est un objet JavaScript. Cela fournit un moyen simple d'obtenir des valeurs du corps de la demande, mais cela ne se produit pas automatiquement.

L'API que vous créez attend des clients qu'ils envoient des données au format JSON dans le corps des requêtes POST et PUT. De plus, les clients doivent définir l'en-tête Content-Type de la demande sur application / json afin que le serveur Web sache quel type de corps de demande est envoyé. Vous pouvez utiliser le middleware intégré express.json pour qu'Express puisse analyser ces requêtes.

Ouvrez le fichier services / web-server.js et ajoutez les lignes suivantes juste en dessous de l'appel app.use, qui ajoute morgan au pipeline de demandes.

 // Parse incoming JSON requests and revive JSON. app.use(express.json({ reviver: reviveJson })); 

Lorsque les données JSON sont analysées en objets JavaScript natifs, seuls les types de données pris en charge dans JSON seront correctement mappés aux types JavaScript. Les dates ne sont pas prises en charge dans JSON et sont généralement représentées sous forme de chaînes ISO 8601. En utilisant la fonction reviver passée au middleware express.json, vous pouvez effectuer la conversion manuellement. Ajoutez le code suivant à la fin du fichier services / web-server.js .

 const iso8601RegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/; function reviveJson(key, value) { // revive ISO 8601 date strings to instances of Date if (typeof value === 'string' && iso8601RegExp.test(value)) { return new Date(value); } else { return value; } } 

Test d'API

Il est temps de tester la nouvelle fonctionnalité CRUD! Jusqu'à présent, vous avez utilisé le navigateur pour tester l'API, mais cela ne fonctionnera pas pour les requêtes POST, PUT et DELETE. Je vais vous montrer comment tester l'API à l'aide des commandes curl car elle est facilement accessible dans une machine virtuelle. Mais vous pouvez utiliser un outil graphique tel que Postman , Insomina (gratuit).

Lancez l'application, puis ouvrez une autre fenêtre de terminal et exécutez la commande suivante pour créer un nouvel employé.

 curl -X "POST" "http://localhost:3000/api/employees" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "IT_PROG", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }' 

Si la demande a abouti, la réponse doit contenir un objet employé avec l'attribut employee_id. Voici un exemple:

image

Dans mon cas, la valeur de employee_id était 227 - vous devrez modifier les commandes suivantes en fonction de la valeur reçue de employee_id.

Par exemple, pour mettre à jour une nouvelle entrée, entrez PUT pour l'URL avec cette valeur d'identifiant.

 curl -X "PUT" "http://localhost:3000/api/employees/227" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "AD_PRES", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }' 

Le déclencheur UPDATE_JOB_HISTORY dans le schéma HR détectera un changement de travail et ajoutera une ligne à la table JOB_HISTORY. Si vous regardez ce tableau, vous devriez voir un enregistrement pour le nouvel employé. Exécutez la commande suivante pour supprimer l'historique des travaux et les enregistrements des employés.

 curl -i -X "DELETE" "http://localhost:3000/api/employees/227" 


Et maintenant, vous avez tout, la pleine fonctionnalité CRUD!

L'API progresse bien, mais il y a du travail à faire. Dans le dernier article, je vais vous montrer comment ajouter des fonctionnalités de pagination, de tri et de filtrage dans les requêtes GET.

Source: https://habr.com/ru/post/fr473560/


All Articles