portaldacalheta.pt
  • Principal
  • Mobile
  • Planification Et Prévision
  • Rise Of Remote
  • Processus Et Outils
Back-End

Création d'une API REST Node.js / TypeScript, partie 1: Express.js



Comment écrire une API REST dans Node.js?

Lors de la création d'un back-end pour une API REST, Express.js est souvent le premier choix parmi les frameworks Node.js. Bien qu'il prenne également en charge la création de modèles HTML et de modèles statiques, dans cette série, nous nous concentrerons sur le développement back-end à l'aide de TypeScript. L'API REST résultante sera celle que n'importe quel framework frontal ou service back-end externe serait en mesure d'interroger.

Tu auras besoin:



  • Connaissance de base de JavaScript et TypeScript
  • Connaissance de base de Node.js
  • Connaissance de base de l'architecture REST (cf. cette section de mon précédent article sur l'API REST si nécessaire)
  • Une installation prête de Node.js (de préférence la version 14+)

Dans un terminal (ou une invite de commande), nous allons créer un dossier pour le projet. À partir de ce dossier, exécutez npm init. Cela créera certains des fichiers de projet de base Node.js dont nous avons besoin.



Ensuite, nous ajouterons le framework Express.js et quelques bibliothèques utiles:



npm install --save express debug winston express-winston cors

Il y a de bonnes raisons pour lesquelles ces bibliothèques sont Développeur Node.js favoris:

logiciel d'exemple de document de conception de système
  • debug est un module que nous allons utiliser pour éviter d'appeler console.log() tout en développant notre application. De cette façon, nous pouvons facilement filtrer les instructions de débogage lors du dépannage. Ils peuvent également être entièrement désactivés en production au lieu de devoir être retirés manuellement.
  • winston est responsable de la journalisation des requêtes vers notre API et des réponses (et erreurs) renvoyées. express-winston s'intègre directement avec Express.js, de sorte que toutes les API standard winston le code de journalisation est déjà terminé.
  • cors est un élément du middleware Express.js qui nous permet d'activer partage de ressources cross-origin . Sans cela, notre API ne serait utilisable qu'à partir des frontaux servis à partir du même sous-domaine que notre back-end.

Notre back-end utilise ces packages lorsqu'il est en cours d'exécution. Mais nous devons également installer certains développement dépendances pour notre configuration TypeScript. Pour cela, nous allons lancer:



npm install --save-dev @types/cors @types/express @types/debug source-map-support tslint typescript

Ces dépendances sont nécessaires pour activer TypeScript pour le propre code de notre application, ainsi que les types utilisés par Express.js et d'autres dépendances. Cela peut faire gagner beaucoup de temps lorsque nous utilisons un IDE comme WebStorm ou VSCode en nous permettant de compléter automatiquement certaines méthodes de fonction lors du codage.

Les dépendances finales dans package.json devrait être comme ça:



'dependencies': { 'debug': '^4.2.0', 'express': '^4.17.1', 'express-winston': '^4.0.5', 'winston': '^3.3.3', 'cors': '^2.8.5' }, 'devDependencies': { '@types/cors': '^2.8.7', '@types/debug': '^4.1.5', '@types/express': '^4.17.2', 'source-map-support': '^0.5.16', 'tslint': '^6.0.0', 'typescript': '^3.7.5' }

Maintenant que toutes nos dépendances requises sont installées, commençons à créer notre propre code!

Structure du projet de l'API REST TypeScript

Pour ce tutoriel, nous allons créer seulement trois fichiers:



  1. ./app.ts
  2. ./common/common.routes.config.ts
  3. ./users/users.routes.config.ts

L'idée derrière les deux dossiers de la structure du projet (common et users) est d'avoir des modules individuels qui ont leurs propres responsabilités. En ce sens, nous allons éventuellement avoir tout ou partie des éléments suivants pour chaque module:

  • Configuration de l'itinéraire pour définir les requêtes que notre API peut traiter
  • Prestations de service pour des tâches telles que la connexion à nos modèles de base de données, l'exécution de requêtes ou la connexion à des services externes requis par la demande spécifique
  • Middleware pour exécuter des validations de requêtes spécifiques avant que le contrôleur final d'une route ne gère ses spécificités
  • Des modèles pour définir des modèles de données correspondant à un schéma de base de données donné, pour faciliter le stockage et la récupération des données
  • Contrôleurs pour séparer la configuration de la route du code qui finalement (après tout middleware) traite une demande de route, appelle les fonctions de service ci-dessus si nécessaire, et donne une réponse au client

Cette structure de dossiers fournit un point de départ précoce pour le reste de cette série de tutoriels et suffisamment pour commencer à pratiquer.



Un fichier de routes communes dans TypeScript

Dans le common dossier, créons le common.routes.config.ts fichier pour ressembler à ce qui suit:

import express from 'express'; export class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; } getName() { return this.name; } }

La façon dont nous créons les itinéraires ici est facultative. Mais puisque nous travaillons avec TypeScript, notre scénario de routes est l'occasion de s'entraîner à utiliser l'héritage avec le extends mot-clé, comme nous le verrons sous peu. Dans ce projet, tous les fichiers de route ont le même comportement: ils ont un nom (que nous utiliserons à des fins de débogage) et accèdent au fichier Express.js principal Application objet.



Maintenant, nous pouvons commencer à créer le fichier d'itinéraire des utilisateurs. Au niveau users dossier, créons users.routes.config.ts et commencez à le coder comme ceci:

import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } }

Ici, nous importons le CommonRoutesConfig class et en l'étendant à notre nouvelle classe, appelée UsersRoutes. Avec le constructeur, nous envoyons l'application (l'objet principal express.Application) et le nom UsersRoutes au constructeur de CommonRoutesConfig.

Cet exemple est assez simple, mais lors de la mise à l'échelle pour créer plusieurs fichiers d'itinéraire, cela nous aidera à éviter le code en double.

Supposons que nous souhaitons ajouter de nouvelles fonctionnalités dans ce fichier, telles que la journalisation. Nous pourrions ajouter le champ nécessaire au CommonRoutesConfig classe, puis toutes les routes qui s'étendent CommonRoutesConfig y aura accès.

Utilisation des fonctions abstraites TypeScript pour des fonctionnalités similaires dans toutes les classes

Et si nous souhaitons avoir des fonctionnalités similaire entre ces classes (comme la configuration des points de terminaison de l'API), mais qui nécessite une implémentation différente pour chaque classe? Une option consiste à utiliser une fonctionnalité TypeScript appelée abstraction .

Créons une fonction abstraite très simple que le UsersRoutes class (et les futures classes de routage) hériteront de CommonRoutesConfig. Disons que nous voulons forcer toutes les routes à avoir une fonction (afin que nous puissions l'appeler à partir de notre constructeur commun) nommée configureRoutes(). C’est là que nous déclarerons les points de terminaison de la ressource de chaque classe de routage.

Pour ce faire, nous allons ajouter trois choses rapides à common.routes.config.ts:

  1. Le mot-clé abstract à notre class line, pour activer l'abstraction pour cette classe.
  2. Une nouvelle déclaration de fonction à la fin de notre classe, abstract configureRoutes(): express.Application;. Cela force toute classe étendant CommonRoutesConfig pour fournir une implémentation correspondant à cette signature - si ce n'est pas le cas, le compilateur TypeScript lèvera une erreur.
  3. Un appel à this.configureRoutes(); à la fin du constructeur, puisque nous pouvons maintenant être sûrs que cette fonction existera.

Le résultat:

import express from 'express'; export abstract class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; this.configureRoutes(); } getName() { return this.name; } abstract configureRoutes(): express.Application; }

Avec cela, toute classe étendant CommonRoutesConfig doit avoir une fonction appelée configureRoutes() qui renvoie un express.Application objet. Cela signifie users.routes.config.ts doit être mis à jour:

import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } configureRoutes() { // (we'll add the actual route configuration here next) return this.app; } }

Pour récapituler ce que nous avons fait:

Nous importons d'abord le common.routes.config fichier, puis le express module. On définit alors le UserRoutes classe, en disant que nous voulons qu'il étende le CommonRoutesConfig classe de base, ce qui implique que nous promettons qu'elle implémentera configureRoutes().

Pour envoyer des informations au CommonRoutesConfig classe, nous utilisons le constructor de la classe. Il s'attend à recevoir le express.Application objet, que nous décrirons plus en détail dans la prochaine étape. Avec super(), nous transmettons au constructeur de CommonRoutesConfig l'application et le nom de nos routes, qui dans ce scénario est UsersRoutes. (super(), à son tour, appellera notre implémentation de configureRoutes().)

Configuration des routes Express.js des points de terminaison des utilisateurs

Le configureRoutes() La fonction est l'endroit où nous allons créer les points de terminaison pour les utilisateurs de notre API REST. Là, nous utiliserons le application et son route fonctionnalités d'Express.js.

L'idée d'utiliser le app.route() La fonction est d’éviter la duplication de code, ce qui est facile puisque nous créons une API REST avec des ressources bien définies. La principale ressource de ce didacticiel est utilisateurs . Nous avons deux cas dans ce scénario:

  • Lorsque l'appelant de l'API souhaite créer un nouvel utilisateur ou répertorier tous les utilisateurs existants, le point de terminaison doit initialement avoir simplement users à la fin du chemin demandé. (Nous n'entrerons pas dans le filtrage des requêtes, la pagination ou d'autres requêtes similaires dans cet article.)
  • Lorsque l'appelant souhaite faire quelque chose de spécifique à un enregistrement utilisateur spécifique, le chemin d'accès aux ressources de la demande suivra le modèle users/:userId.

Le chemin .route() fonctionne dans Express.js nous permet de gérer les verbes HTTP avec un chaînage élégant. En effet, .get(), .post(), etc., tous renvoient la même instance de IRoute que le premier .route() l'appel fait. La configuration finale sera comme ceci:

configureRoutes() { this.app.route(`/users`) .get((req: express.Request, res: express.Response) => { res.status(200).send(`List of users`); }) .post((req: express.Request, res: express.Response) => { res.status(200).send(`Post to users`); }); this.app.route(`/users/:userId`) .all((req: express.Request, res: express.Response, next: express.NextFunction) => { // this middleware function runs before any request to /users/:userId // but it doesn't accomplish anything just yet--- // it simply passes control to the next applicable function below using next() next(); }) .get((req: express.Request, res: express.Response) => { res.status(200).send(`GET requested for id ${req.params.userId}`); }) .put((req: express.Request, res: express.Response) => { res.status(200).send(`PUT requested for id ${req.params.userId}`); }) .patch((req: express.Request, res: express.Response) => { res.status(200).send(`PATCH requested for id ${req.params.userId}`); }) .delete((req: express.Request, res: express.Response) => { res.status(200).send(`DELETE requested for id ${req.params.userId}`); }); return this.app; }

Le code ci-dessus permet à tout client d'API REST d'appeler notre users extrémité avec un POST ou un GET demande. De même, il permet à un client d'appeler notre /users/:userId extrémité avec un GET, PUT, PATCH ou DELETE demande.

Mais pour /users/:userId, nous avons également ajouté un middleware générique à l'aide de all() , qui sera exécutée avant l'une des fonctions get(), put(), patch() ou delete() les fonctions. Cette fonction sera utile lorsque (plus tard dans la série) nous créerons des routes destinées à être accessibles uniquement par des utilisateurs authentifiés.

pourquoi le nœud js est utilisé

Vous avez peut-être remarqué que dans notre .all() function - comme avec n'importe quel middleware - nous avons trois types de champs: Request, Response et NextFunction.

  • La Demande est la façon dont Express.js représente la requête HTTP à traiter. Ce type met à niveau et étend la Node.js natif type de demande.
  • La Réponse est également la façon dont Express.js représente la réponse HTTP, étendant à nouveau le Node.js natif type de réponse.
  • Non moins important, le NextFunction sert de fonction de rappel, permettant au contrôle de passer par toutes les autres fonctions du middleware. En cours de route, tous les middleware partageront les mêmes objets de requête et de réponse avant que le contrôleur ne renvoie finalement une réponse au demandeur.

Notre fichier de point d'entrée Node.js, app.ts

Maintenant que nous avons configuré certains squelettes de routes de base, nous allons commencer à configurer le point d'entrée de l'application. Créons le app.ts fichier à la racine de notre dossier de projet et commencez-le avec ce code:

import express from 'express'; import * as http from 'http'; import * as bodyparser from 'body-parser'; import * as winston from 'winston'; import * as expressWinston from 'express-winston'; import cors from 'cors'; import {CommonRoutesConfig} from './common/common.routes.config'; import {UsersRoutes} from './users/users.routes.config'; import debug from 'debug';

Seules deux de ces importations sont nouvelles à ce stade de l'article:

  • http est un module natif de Node.js. Il est nécessaire pour démarrer notre application Express.js.
  • body-parser est un middleware fourni avec Express.js. Il analyse la demande (dans notre cas, comme JSON) avant que le contrôle ne passe à nos propres gestionnaires de demandes.

Maintenant que nous avons importé les fichiers, nous allons commencer à déclarer les variables que nous voulons utiliser:

const app: express.Application = express(); const server: http.Server = http.createServer(app); const port: Number = 3000; const routes: Array = []; const debugLog: debug.IDebugger = debug('app');

Le express() La fonction retourne l'objet principal de l'application Express.js que nous transmettrons à travers notre code, en commençant par l'ajouter au http.Server objet. (Nous devrons démarrer le http.Server après avoir configuré notre express.Application.)

Nous écouterons sur le port 3000 au lieu des ports standard 80 (HTTP) ou 443 (HTTPS), car ceux-ci seraient généralement utilisés pour le front-end d'une application.

Pourquoi Port 3000?

Il n’existe pas de règle selon laquelle le port doit être 3 000 - s’il n’est pas spécifié, un port arbitraire sera assigné - mais 3000 est utilisé dans les exemples de documentation pour Node.js et Express.js, donc nous continuons la tradition ici.

Node.js peut-il partager des ports avec le serveur frontal?

Nous pouvons toujours fonctionner localement sur un port personnalisé, même lorsque nous voulons que notre back-end réponde aux demandes sur les ports standard. Cela nécessiterait un proxy inverse pour recevoir des requêtes sur le port 80 ou 443 avec un domaine ou un sous-domaine spécifique. Il les redirigerait ensuite vers notre port interne 3000.

Le routes array gardera une trace de nos fichiers de routes à des fins de débogage, comme nous le verrons ci-dessous.

Enfin, debugLog se terminera par une fonction similaire à console.log, mais en mieux: c'est plus facile à régler car elle est automatiquement étendue à ce que nous voulons appeler notre contexte de fichier / module. (Dans ce cas, nous l'avons appelé 'app' lorsque nous l'avons passé dans une chaîne au constructeur debug().)

Nous sommes maintenant prêts à configurer tous nos modules middleware Express.js et les routes de notre API:

// here we are adding middleware to parse all incoming requests as JSON app.use(bodyparser.json()); // here we are adding middleware to allow cross-origin requests app.use(cors()); // here we are configuring the expressWinston logging middleware, // which will automatically log all HTTP requests handled by Express.js app.use(expressWinston.logger({ transports: [ new winston.transports.Console() ], format: winston.format.combine( winston.format.colorize(), winston.format.json() ) })); // here we are adding the UserRoutes to our array, // after sending the Express.js application object to have the routes added to our app! routes.push(new UsersRoutes(app)); // here we are configuring the expressWinston error-logging middleware, // which doesn't *handle* errors per se, but does *log* them app.use(expressWinston.errorLogger({ transports: [ new winston.transports.Console() ], format: winston.format.combine( winston.format.colorize(), winston.format.json() ) })); // this is a simple route to make sure everything is working properly app.get('/', (req: express.Request, res: express.Response) => { res.status(200).send(`Server up and running!`) });

Vous avez peut-être remarqué que le expressWinston.errorLogger est réglé après nous définissons nos itinéraires. Ce n'est pas une erreur! Comme le documentation express-winston États:

L'enregistreur doit être ajouté APRÈS le routeur express (app.router) et AVANT l'un de vos gestionnaires d'erreurs personnalisés (express.handler).

Enfin et surtout:

server.listen(port, () => { debugLog(`Server running at http://localhost:${port}`); routes.forEach((route: CommonRoutesConfig) => { debugLog(`Routes configured for ${route.getName()}`); }); });

Cela démarre en fait notre serveur. Une fois lancé, Node.js exécutera notre fonction de rappel, qui signale que nous sommes en cours d’exécution, suivie des noms de toutes les routes que nous avons configurées - jusqu’à présent, juste UsersRoutes.

Mise à jour package.json pour transpiler TypeScript en JavaScript et exécuter l'application

Maintenant que notre squelette est prêt à fonctionner, nous avons d'abord besoin d'une configuration standard pour activer la transpilation TypeScript. Ajoutons le fichier tsconfig.json à la racine du projet:

{ 'compilerOptions': { 'target': 'es2016', 'module': 'commonjs', 'outDir': './dist', 'strict': true, 'esModuleInterop': true, 'inlineSourceMap': true } }

Ensuite, il suffit d'ajouter la touche finale à package.json sous la forme des scripts suivants:

fiscalité c corp vs s corp
'scripts': { 'start': 'tsc && node ./dist/app.js', 'debug': 'export DEBUG=* && npm run start', 'test': 'echo 'Error: no test specified' && exit 1' },

Le test script est un espace réservé que nous remplacerons plus tard dans la série.

requête média pour tous les appareils mobiles

La tsc dans le start script appartient à TypeScript. Il est responsable de la transpilation de notre code TypeScript en JavaScript, qu'il affichera dans le dist dossier. Ensuite, nous exécutons simplement la version construite avec node ./dist/app.js.

Le debug script appelle le start script mais définit d'abord un DEBUG variable d'environnement. Cela a pour effet d'activer tous nos debugLog() instructions (plus d'autres similaires à partir d'Express.js lui-même, qui utilise le même module debug que nous faisons) pour afficher des détails utiles au terminal - des détails qui sont (commodément) autrement cachés lors de l'exécution du serveur en mode de production avec un standard npm start.

Essayez d'exécuter npm run debug vous-même, puis comparez cela avec npm start pour voir comment la sortie de la console change.

Astuce: vous pouvez limiter la sortie de débogage à notre app.ts propre fichier debugLog() instructions utilisant DEBUG=app au lieu de DEBUG=*. Le debug module est généralement assez flexible, et cette fonctionnalité ne fait pas exception .

Les utilisateurs de Windows devront probablement changer le export à SET depuis export est comment cela fonctionne sur Mac et Linux. Si votre projet doit prendre en charge plusieurs environnements de développement, le package cross-env fournit une solution simple ici.

Test du back-end Live Express.js

Avec npm run debug ou npm start toujours en cours, notre API REST sera prête à traiter les requêtes sur le port 3000. À ce stade, nous pouvons utiliser cURL, Facteur , Insomnie , etc. pour tester le back-end.

Étant donné que nous n'avons créé qu'un squelette pour la ressource utilisateurs, nous pouvons simplement envoyer des requêtes sans corps pour voir que tout fonctionne comme prévu. Par exemple:

curl --location --request GET 'localhost:3000/users/12345'

Notre back-end devrait renvoyer la réponse GET requested for id 12345.

Quant à POST ing:

curl --location --request POST 'localhost:3000/users' --data-raw ''

Ceci et tous les autres types de requêtes pour lesquelles nous avons construit des squelettes seront assez similaires.

Prêt pour le développement d'API REST Rapid Node.js avec TypeScript

Dans cet article, nous avons commencé à créer une API REST en configurant le projet à partir de zéro et en plongeant dans les bases du framework Express.js. Ensuite, nous avons fait notre premier pas vers la maîtrise de TypeScript en créant un modèle avec UsersRoutesConfig étendant CommonRoutesConfig, un modèle que nous réutiliserons pour le prochain article de cette série. Nous avons terminé en configurant notre app.ts point d'entrée pour utiliser nos nouvelles routes et package.json avec des scripts pour construire et exécuter notre application.

Mais même les bases d'une API REST créée avec Express.js et TypeScript sont assez impliquées. Dans la partie suivante de cette série, nous nous concentrons sur la création de contrôleurs appropriés pour la ressource des utilisateurs et explorons quelques modèles utiles pour les services, les intergiciels, les contrôleurs et les modèles.

Le projet complet est disponible sur GitHub , et le code à la fin de cet article se trouve dans le toptal-article-01 branche.

Comprendre les bases

Puis-je utiliser TypeScript avec Node.js?

Absolument! Il est très courant que les packages npm populaires (y compris Express.js) aient des fichiers de définition de type TypeScript correspondants. C'est vrai à propos de Node.js lui-même, ainsi que des sous-composants inclus comme son package de débogage.

Node.js est-il bon pour les API REST?

Oui. Node.js peut être utilisé seul pour créer des API REST prêtes pour la production, et il existe également plusieurs frameworks populaires comme Express.js pour réduire l'inévitable passe-partout.

TypeScript est-il difficile à apprendre?

Non, il n'est pas difficile de commencer à apprendre TypeScript pour ceux qui ont une expérience JavaScript moderne. C'est encore plus facile pour ceux qui ont de l'expérience en programmation orientée objet. Mais maîtriser toutes les nuances et les meilleures pratiques de TypeScript prend du temps, comme pour toute compétence.

Dois-je utiliser TypeScript?

Cela dépend du projet, mais c'est certainement recommandé pour la programmation Node.js. C'est un langage plus expressif pour modéliser des domaines problématiques du monde réel sur le back-end. Cela rend le code plus lisible et réduit le potentiel de bogues.

À quoi sert TypeScript?

TypeScript est utilisé partout où JavaScript est trouvé, mais il est particulièrement bien adapté aux applications plus importantes. Il utilise JavaScript comme base, ajoutant un typage statique et une bien meilleure prise en charge du paradigme de la programmation orientée objet (POO). Ceci, à son tour, prend en charge une expérience de développement et de débogage plus avancée.

Exploration de données pour l'analyse prédictive des réseaux sociaux

Science Des Données Et Bases De Données

Exploration de données pour l'analyse prédictive des réseaux sociaux
Directeur de l'offre de talents

Directeur de l'offre de talents

Autre

Articles Populaires
Composez le 996 pour la fatigue: comment donner la priorité à éviter l'épuisement professionnel
Composez le 996 pour la fatigue: comment donner la priorité à éviter l'épuisement professionnel
Illustration de la marque 101: Visualiser le récit
Illustration de la marque 101: Visualiser le récit
L'avenir de l'UX est notre humanité
L'avenir de l'UX est notre humanité
Les principes de base de la refonte d'un site Web - Une étude de cas
Les principes de base de la refonte d'un site Web - Une étude de cas
Comment mettre en œuvre une conception d'interface utilisateur iOS parfaite au pixel près
Comment mettre en œuvre une conception d'interface utilisateur iOS parfaite au pixel près
 
Conception d'une VUI - Interface utilisateur vocale
Conception d'une VUI - Interface utilisateur vocale
Maîtriser la croissance durable des startups et trouver vos indicateurs de performance clés pertinents
Maîtriser la croissance durable des startups et trouver vos indicateurs de performance clés pertinents
Les forces et les avantages des micro-frontends
Les forces et les avantages des micro-frontends
Transformation de quantification moyenne successive optimisée
Transformation de quantification moyenne successive optimisée
Inspirez-vous de ces visualisations de données
Inspirez-vous de ces visualisations de données
Articles Populaires
  • mot de modèle de document de conception de logiciel
  • python ajouter un attribut à l'objet
  • aller au cadre de stratégie de marché
  • différence entre une société ac et une société s
  • ar contre vr contre mr
  • développeurs flash et animateurs flash
Catégories
  • Mobile
  • Planification Et Prévision
  • Rise Of Remote
  • Processus Et Outils
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt