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

Partie 5. Création d'une API REST: pagination, tri manuel et filtrage

Dans l' article précédent, vous avez terminé la création des fonctionnalités de base de l'API CRUD.

Et maintenant, lorsqu'une demande HTTP GET est émise sur la route des employés, toutes les lignes de la table sont retournées. Cela peut ne pas avoir beaucoup d'importance avec seulement 107 lignes dans la table HR.EMPLOYEES, mais imaginez ce qui se passerait si la table contient des milliers ou des millions de lignes. Les clients tels que les applications mobiles et Web affichent généralement seulement une fraction des lignes disponibles dans la base de données, puis sélectionnent plus de lignes si nécessaire - peut-être lorsque l'utilisateur fait défiler vers le bas ou clique sur le bouton "Suivant" sur un contrôle de pause aux pages de l'interface utilisateur.

Pour cela, les API REST doivent prendre en charge les outils de pagination pour les résultats renvoyés. Une fois la pagination prise en charge, les capacités de tri deviennent nécessaires, car les données doivent généralement être triées avant l'application de la pagination. De plus, l'outil de filtrage des données est très important pour les performances. Pourquoi envoyer des données de la base de données, à travers la couche intermédiaire et complètement au client, si cela n'est pas nécessaire?

J'utiliserai les paramètres de chaîne de requête URL afin que les clients puissent spécifier comment les résultats doivent être paginés, triés et filtrés. Comme toujours en programmation, l'implémentation peut varier selon vos besoins, vos objectifs de performances, etc. Dans cet article, je vais vous parler d'une approche manuelle pour ajouter ces fonctions à l'API.

Pagination

Recherchez les paramètres de chaîne que j'utiliserai pour la pagination: sauter et limiter. Le paramètre skip sera utilisé pour ignorer le nombre de lignes spécifié, tandis que limit limitera le nombre de lignes retournées. J'utiliserai la valeur par défaut de 30 pour la limite si le client ne fournit pas la valeur.

Commencez par mettre à jour la logique du contrôleur pour extraire les valeurs de la chaîne de requête et passez-les à l'API de base de données. Ouvrez le fichier controllers / employee.js et ajoutez les lignes de code suivantes à la fonction get après la ligne qui analyse le paramètre req.params.id.

// *** line that parses req.params.id is here *** context.skip = parseInt(req.query.skip, 10); context.limit = parseInt(req.query.limit, 10); 

Vous devez maintenant mettre à jour la logique de la base de données pour prendre en compte ces valeurs et mettre à jour la requête SQL en conséquence. En SQL, la clause offset est utilisée pour ignorer les lignes et la clause fetch est utilisée pour limiter le nombre de lignes renvoyées par la requête. Comme d'habitude, les valeurs ne seront pas ajoutées directement à la requête - à la place, elles seront ajoutées en tant que variables de liaison pour des raisons de performances et de sécurité. Ouvrez db_apis / employee.js et ajoutez le code suivant après le bloc if dans la fonction find, qui ajoute la clause where à la demande.

 // *** if block that appends where clause ends here *** if (context.skip) { binds.row_offset = context.skip; query += '\noffset :row_offset rows'; } const limit = (context.limit > 0) ? context.limit : 30; binds.row_limit = limit; query += '\nfetch next :row_limit rows only'; 

C'est tout ce que vous devez faire pour paginer! Lancez l'API, puis exécutez quelques commandes URL dans un autre terminal pour la tester. Voici quelques exemples que vous pouvez utiliser:

 # use default limit (30) curl "http://localhost:3000/api/employees" # set limit to 5 curl "http://localhost:3000/api/employees?limit=5" # use default limit and set skip to 5 curl "http://localhost:3000/api/employees?skip=5" # set both skip and limit to 5 curl "http://localhost:3000/api/employees?skip=5&limit=5" 

Tri

Au minimum, les clients doivent pouvoir spécifier une colonne à trier et à ordonner (ascendante ou descendante). La façon la plus simple de le faire est de définir un paramètre de requête (j'utiliserai sort), qui vous permet de passer une chaîne comme 'last_name: asc' ou 'salaire: desc'. La seule façon de garantir l'ordre de l'ensemble de résultats renvoyé par la requête SQL est d'inclure la clause order by. Pour cette raison, il serait bien d'avoir une définition de commande par défaut définie pour garantir la cohérence lorsque le client ne la spécifie pas.

Revenez à controllers / employee.js et ajoutez la ligne de code suivante à la fonction get après la ligne qui analyse le paramètre req.query.limit.

 // *** line that parses req.query.limit is here *** context.sort = req.query.sort; 

Ouvrez ensuite db_apis / employee.js et ajoutez la ligne suivante sous les lignes qui déclarent et initialisent baseQuery.

 // *** lines that initalize baseQuery end here *** const sortableColumns = ['id', 'last_name', 'email', 'hire_date', 'salary']; 

sortableColumns est une liste blanche de colonnes que les clients peuvent utiliser pour trier. Ensuite, à l'intérieur de la fonction find, ajoutez le bloc if suivant, qui ajoute la clause order by. Cela doit être fait après l'ajout de la clause where, mais avant les clauses offset et fetch.

 // *** if block that appends where clause ends here *** if (context.sort === undefined) { query += '\norder by last_name asc'; } else { let [column, order] = context.sort.split(':'); if (!sortableColumns.includes(column)) { throw new Error('Invalid "sort" column'); } if (order === undefined) { order = 'asc'; } if (order !== 'asc' && order !== 'desc') { throw new Error('Invalid "sort" order'); } query += `\norder by "${column}" ${order}`; } 

La première partie du bloc if vérifie si le client a réussi la valeur de tri. Si ce n'est pas le cas, la clause order by par défaut est ajoutée à la requête SQL, qui trie par nom de famille dans l'ordre croissant. Si une valeur de tri est spécifiée, elle est d'abord divisée en valeurs de colonne et d'ordre, et chaque valeur est vérifiée avant d'ajouter l'ordre par à la requête.

Vous pouvez maintenant exécuter plusieurs commandes URL pour le valider. Voici quelques exemples à essayer:

 # use default sort (last_name asc) curl "http://localhost:3000/api/employees" # sort by id and use default direction (asc) curl "http://localhost:3000/api/employees?sort=id" # sort by hire_date desc curl "http://localhost:3000/api/employees?sort=hire_date:desc" # use sort with limit and skip together curl "http://localhost:3000/api/employees?limit=5&skip=5&sort=salary:desc" # should throw an error because first_name is not whitelisted curl "http://localhost:3000/api/employees?sort=first_name:desc" # should throw an error because 'other' is not a valid order curl "http://localhost:3000/api/employees?sort=last_name:other" 

Les deux derniers exemples devraient lever des exceptions, car ils contiennent des valeurs qui n'ont pas été ajoutées à la liste blanche. Il utilise le gestionnaire d'erreurs Express standard, donc l'erreur est renvoyée sous forme de page Web HTML.

Filtrage

La possibilité de filtrer les données est une caractéristique importante que toutes les API REST doivent fournir. Comme pour le tri, l'implémentation peut être simple ou complexe selon ce que vous souhaitez prendre en charge. L'approche la plus simple consiste à ajouter la prise en charge des filtres de correspondance complète (par exemple, last_name = Doe). Des implémentations plus complexes peuvent ajouter la prise en charge des opérateurs de base (par exemple, <,>, instr, etc.) et des opérateurs logiques complexes (par exemple, et / ou) qui peuvent regrouper plusieurs filtres.

Dans cet article, je vais essayer de simplifier la situation et d'ajouter un support de filtre pour seulement deux colonnes: department_id et manager_id. Pour chaque colonne, j'activerai le paramètre correspondant dans la chaîne de requête. La logique de base de données qui ajoute la clause where lorsque les demandes GET sont envoyées au point de terminaison avec un seul employé doit être mise à jour pour prendre en compte ces nouveaux filtres.

Ouvrez controllers / employee.js et ajoutez les lignes suivantes sous la ligne qui analyse la valeur req.query.sort dans la fonction get.

 // *** line that parses req.query.sort is here *** context.department_id = parseInt(req.query.department_id, 10); context.manager_id = parseInt(req.query.manager_id, 10); 

Modifiez ensuite db_apis / employee.js pour ajouter la phrase 1 = 1 à la requête de base, comme indiqué ci-dessous.

 const baseQuery = `select employee_id "id", 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" from employees where 1 = 1`; 

Bien sûr, 1 = 1 sera toujours vrai, donc l'optimiseur l'ignorera simplement. Cependant, cette méthode simplifiera l'ajout de prédicats supplémentaires à l'avenir.

Dans la fonction find, remplacez le bloc if, qui ajoute la clause where lors du passage context.id, par les lignes suivantes.

 // *** line that declares 'binds' is here *** if (context.id) { binds.employee_id = context.id; query += '\nand employee_id = :employee_id'; } if (context.department_id) { binds.department_id = context.department_id; query += '\nand department_id = :department_id'; } if (context.manager_id) { binds.manager_id = context.manager_id; query += '\nand manager_id = :manager_id'; } 

Comme vous pouvez le voir, chaque bloc if ajoute simplement la valeur transmise à l'objet binds, puis ajoute le prédicat correspondant à la clause where. Enregistrez les modifications et redémarrez l'API. Utilisez ensuite ces commandes URL pour vérifier ceci:

 # filter where department_id = 90 (returns 3 employees) curl "http://localhost:3000/api/employees?department_id=90" # filter where manager_id = 100 (returns 14 employees) curl "http://localhost:3000/api/employees?manager_id=100" # filter where department_id = 90 and manager_id = 100 (returns 2 employees) curl "http://localhost:3000/api/employees?department_id=90&manager_id=100" 

Voilà, l'API prend désormais en charge la pagination, le tri et le filtrage! Une approche manuelle offre beaucoup de contrôle, mais nécessite beaucoup de code. La fonction de recherche compte désormais 58 lignes et ne prend en charge que des capacités de tri et de filtrage limitées. Vous pourriez envisager d'utiliser un module, tel que le générateur de requêtes Knex.js , pour simplifier ces opérations.

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


All Articles