Fermer

décembre 6, 2018

Comment utiliser TypeScript pour créer une API de nœud avec Express –

ES6 en action: let et const


Qu'on le veuille ou non, JavaScript aide les développeurs à alimenter Internet depuis 1995. Depuis cette époque, l'utilisation de JavaScript passait de simples améliorations de l'expérience utilisateur à des applications complexes fonctionnant en pile complète utilisant Node.js sur le serveur et l'un des nombreux frameworks. sur un client tel que Angular, React ou Vue.

Aujourd'hui, la construction d'applications JavaScript à l'échelle reste un défi. De plus en plus d'équipes se tournent vers TypeScript pour compléter leurs projets JavaScript.

Les applications serveur Node.js peuvent également tirer parti de l'utilisation de TypeScript. Le but de ce didacticiel est de vous montrer comment construire une nouvelle application Node.js à l'aide de TypeScript et Express.

L'affaire de TypeScript

En tant que développeur Web, j'ai depuis longtemps cessé de résister à JavaScript et je me suis développé pour l'apprécier sa flexibilité et son omniprésence. Les fonctionnalités linguistiques ajoutées à ES2015 et aux versions ultérieures ont considérablement amélioré son utilité et réduit les frustrations liées à l'écriture d'applications.

Cependant, les projets JavaScript plus volumineux nécessitent des outils tels qu'ESLint pour détecter les erreurs courantes et une plus grande discipline pour saturer la base de code d'essais utiles. Comme pour tout projet logiciel, une culture d'équipe saine comprenant un processus de révision par des pairs peut améliorer la qualité et éviter les problèmes susceptibles de se glisser dans un projet.

Le principal avantage de l'utilisation de TypeScript est de détecter davantage d'erreurs avant leur mise en production. facilite le travail avec votre base de code.

TypeScript n'est pas un langage différent. C’est un surensemble flexible de JavaScript avec des façons de décrire des types de données facultatifs. Tout JavaScript «standard» et valide est également valide TypeScript. Vous pouvez utiliser autant de ressources que vous le souhaitez.

Dès que vous ajoutez le compilateur TypeScript ou un plugin TypeScript à votre éditeur de code préféré, la sécurité et la productivité procurent des avantages immédiats. TypeScript peut vous alerter des fonctions et propriétés mal orthographiées, détecter la transmission des types d'arguments incorrects ou du nombre d'arguments aux fonctions incorrect, et fournir des suggestions plus complètes de complétion automatique.

Construire une application d'inventaire de la guitare avec TypeScript et Node.js

Parmi les guitaristes, il y a une blague que tout le monde devrait comprendre .

Q: «De combien de guitares avez-vous besoin de ?"

A: “ n + 1. Toujours un de plus. ”

Dans ce tutoriel, vous allez créer une nouvelle application Node.js pour suivre un inventaire des guitares. En résumé, ce didacticiel utilise Node.js avec Express EJS et PostgreSQL au dos, . Materialise et Axios sur le front-office, Okta pour l'enregistrement et l'autorisation de compte, et TypeScript pour régir les JavaScripts! [19659002]  Démo Guitar Inventory

Créez votre projet Node.js

Ouvrez un terminal (Mac / Linux) ou une invite de commande (Windows) et tapez la commande suivante:

 node --version

Si vous obtenez une erreur ou si la version de Node.js que vous possédez est inférieure à la version 8, vous devrez installer Node.js. Sur Mac ou Linux, je vous recommande d’installer d’abord nvm et d’utiliser nvm pour installer Node.js. Sous Windows, je vous recommande d'utiliser Chocolatey .

Après vous être assuré d'avoir une version récente de Node.js installée, créez un dossier pour votre projet.

 mkdir guitar-inventory
cd guitare-inventaire

Utilisez npm pour initialiser un fichier package.json .

 npm init -y

Bonjour, monde!

Dans cet exemple d'application, Express est utilisé pour servir des pages Web et implémenter une API. Les dépendances sont installées en utilisant npm . Ajoutez Express à votre projet avec la commande suivante:

 npm install express

Ouvrez ensuite le projet dans l’éditeur de votre choix

Si vous n’avez pas encore d’éditeur de code favori, j’utilise et recommande Code Visual Studio . VS Code prend en charge de manière exceptionnelle JavaScript et Node.js, tels que l’achèvement de code intelligent et le débogage, et une vaste bibliothèque d’extensions gratuites fournies par la communauté.

Créez un dossier nommé src . Dans ce dossier, créez un fichier nommé index.js . Ouvrez le fichier et ajoutez le code JavaScript suivant:

 const express = require ("express");
const app = express ();
port constant = 8080; // port par défaut pour écouter

// définir un gestionnaire de route pour la page d'accueil par défaut
app.get ("/", (req, res) => {
    res.send ("Hello world!");
});

// démarre le serveur Express
app.listen (port, () => {
    console.log (`le serveur a démarré à http: // localhost: $ {port}`);
});

Ensuite, mettez à jour package.json pour indiquer à npm de savoir comment exécuter votre application. Modifiez la valeur de la propriété principale pour qu'elle pointe sur src / index.js et ajoutez un script start à l'objet scripts . [19659030] "main": "src / index.js",
  "scripts": {
    "start": "node.",
    "test": "echo " Erreur: aucun test spécifié "&& exit 1"
  },

Maintenant, depuis le terminal ou la ligne de commande, vous pouvez lancer l'application.

 npm run start

Si tout se passe bien, ce message devrait être écrit sur la console.

 Le serveur a été démarré à l'adresse http: // localhost: 8080.

Lancez votre navigateur et accédez à http: // localhost: 8080 . Vous devriez voir le texte «Hello world!»

 Hello World

Remarque: pour arrêter l'application Web, vous pouvez revenir au guide ou à l'invite de commande et appuyer sur CTRL + C. .

Configurez votre projet Node.js pour utiliser TypeScript

La première étape consiste à ajouter le compilateur TypeScript. Vous pouvez installer le compilateur en tant que dépendance de développeur à l'aide de l'indicateur - save-dev .

 npm install --save-dev typescript

L'étape suivante consiste à ajouter un fichier tsconfig.json . Ce fichier indique à TypeScript comment compiler (transpiler) votre code TypeScript en code JavaScript simple.

Créez un fichier nommé tsconfig.json dans le dossier racine de votre projet et ajoutez la configuration suivante:

 {
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "cible": "es6",
        "noImplicitAny": true,
        "moduleResolution": "noeud",
        "sourceMap": true,
        "outDir": "dist",
        "baseUrl": ".",
        "chemins": {
            "*": [
                "node_modules/*"
            ]
        }
    },
    "inclure": [
        "src/**/*"
    ]
}

Sur la base de ce fichier tsconfig.json le compilateur TypeScript compilera tous les fichiers se terminant par .ts qu'il trouve dans le dossier src . et stockez les résultats dans un dossier nommé dist . Node.js utilise le système de module CommonJS, la valeur du paramètre module est donc commonjs . En outre, la version cible de JavaScript est ES6 (ES2015), qui est compatible avec les versions modernes de Node.js.

C'est également une excellente idée d'ajouter tslint et de créer un tslint.json qui indique à TypeScript comment lint votre code. Si vous ne connaissez pas les éléments, c'est un outil d'analyse de code qui vous avertit des problèmes potentiels de votre code, autres que les problèmes de syntaxe.

Installez tslint en tant que dépendance du développeur.

 npm install - -save-dev dactylographiée tslint

Créez ensuite un nouveau fichier dans le dossier racine nommé fichier tslint.json puis ajoutez la configuration suivante:

 {
    "defaultSeverity": "erreur",
    "étend": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "règles": {
        "fin de virgule": [ false ]
    },
    "rulesDirectory": []
}

Ensuite, mettez à jour votre package.json pour qu'il remplace main par le nouveau dossier dist créé par le compilateur TypeScript. Ajoutez également quelques scripts pour exécuter TSLint et le compilateur TypeScript juste avant de démarrer le serveur Node.js.

 "main": "dist / index.js",
  "scripts": {
    "prebuild": "tslint -c tslint.json -p tsconfig.json --fix",
    "build": "tsc",
    "prestart": "npm run build",
    "start": "node.",
    "test": "echo " Erreur: aucun test spécifié  "&& exit 1"
  },

Enfin, modifiez l'extension du fichier src / index.js de .js à .ts l'extension TypeScript, puis exécutez le script de démarrage. .

 npm run start

Remarque: Vous pouvez exécuter TSLint et le compilateur TypeScript sans démarrer le serveur Node.js à l'aide de npm run build .

Erreurs TypeScript

Oh no! Vous remarquerez peut-être tout de suite quelques erreurs enregistrées sur la console.

 ERREUR: /Users/reverentgeek/Projects/guitar-inventory/src/index.ts[12, 5]: Les appels à 'console.log' ne sont pas autorisés. .

src / index.ts: 1: 17 - erreur TS2580: Impossible de trouver le nom 'require'. Avez-vous besoin d'installer des définitions de type pour le noeud? Essayez `npm i @ types / node`.

1 const express = require ("express");
                  ~~~~~~~

src / index.ts: 6: 17 - erreur TS7006: le paramètre 'req' a implicitement un type 'quelconque'.

6 app.get ("/", (req, res) => {
                  ~~~

Les deux erreurs les plus courantes que vous pouvez voir sont les erreurs de syntaxe et les informations de type manquantes. TSLint considère que l'utilisation de console.log constitue un problème pour le code de production. La meilleure solution consiste à remplacer les utilisations de console.log par un cadre de journalisation tel que winston . Pour l'instant, ajoutez le commentaire suivant à src / index.ts pour désactiver la règle.

 app.listen (port, () => {
    // tslint: disable-next-line: no-console
    console.log (`le serveur a démarré à http: // localhost: $ {port}`);
});

TypeScript préfère utiliser la syntaxe du module import sur Requiert vous commencerez donc par modifier la première ligne de src / index.ts de:

 const express = require ("express");

à:

 import express de "express";

Trouver les bons types

Pour aider les développeurs de TypeScript, les auteurs de bibliothèques et les contributeurs à la communauté publient des bibliothèques associées appelées fichiers de déclaration TypeScript . Les fichiers de déclaration sont publiés dans le référentiel open source DefinitelyTyped ou se trouvent parfois dans la bibliothèque JavaScript d'origine.

Mettez à jour votre projet afin que TypeScript puisse utiliser les déclarations de type de Node.js et Express.

 ] npm installer --save-dev @ types / noeud @ types / express

Ensuite, réexécutez le script de démarrage et vérifiez qu'il n'y a plus d'erreur.

 npm run start

Créez une meilleure interface utilisateur avec Materialise et EJS

Votre application Node.js a bien démarré, mais n'est peut-être pas la plus belle qui soit. Cette étape ajoute Materialise un framework CSS moderne basé sur la conception de matériaux de Google, et Modèles de JavaScript incorporés (EJS), un langage de modèles HTML pour Express. Materialise et EJS constituent une bonne base pour une interface utilisateur bien meilleure.

Commencez par installer EJS en tant que dépendance.

 npm install ejs

Ensuite, créez un nouveau dossier sous / src nommé vues . Dans le dossier / src / views créez un fichier nommé index.ejs . Ajoutez le code suivant à /src/views/index.ejs .




    
    
     Inventaire des guitares 
    
    
    


    

Inventaire des guitares

arrow_forward Lancez-vous!     
%MINIFYHTML744903a3114d8f3d2708d5715a9255789%

Mise à jour /src/index.ts avec le code suivant:

 import express de "express";
importer le chemin de "chemin";
const app = express ();
port constant = 8080; // port par défaut pour écouter

// Configurer Express pour utiliser EJS
app.set ("vues", chemin.join (__dirname, "vues"));
app.set ("moteur de vue", "ejs");

// définir un gestionnaire de route pour la page d'accueil par défaut
app.get ("/", (req, res) => {
    // rendre le modèle d'index
    res.render ("index");
});

// démarre le serveur express
app.listen (port, () => {
    // tslint: disable-next-line: no-console
    console.log (`le serveur a démarré à http: // localhost: $ {port}`);
});

Ajouter un script de génération d'actif pour Typescript

Le compilateur TypeScript consiste à générer les fichiers JavaScript et à les copier dans le dossier dist . Cependant, il ne copie pas les autres types de fichiers que le projet doit exécuter, tels que les modèles de vue EJS. Pour ce faire, créez un script de construction qui copie tous les autres fichiers dans le dossier dist .

Installez les modules nécessaires et les déclarations TypeScript à l'aide de ces commandes.

 npm install --save-dev ts -node shelljs fs-extra nodemon rimraf npm-run-all
npm installer --save-dev @ types / fs-extra @ types / shelljs

Voici un bref aperçu des modules que vous venez d'installer.

  1. ts-node . Utilisé pour exécuter des fichiers TypeScript directement.
  2. shelljs . Utilisé pour exécuter des commandes de shell telles que copier des fichiers et supprimer des répertoires.
  3. fs-extra . Module qui étend le module du système de fichiers Node.js ( fs ) avec des fonctionnalités telles que la lecture et l'écriture de fichiers JSON.
  4. rimraf . Permet de supprimer récursivement les dossiers.
  5. npm-run-all . Utilisé pour exécuter plusieurs scripts npm de manière séquentielle ou parallèle
  6. nodemon . Un outil pratique pour exécuter Node.js dans un environnement de développement. Nodemon surveille les fichiers pour les modifications et redémarre automatiquement l'application Node.js lorsque des modifications sont détectées. Plus besoin d'arrêter et de redémarrer Node.js!

Créez un nouveau dossier à la racine du projet nommé tools . Créez un fichier dans le dossier tools nommé copyAssets.ts . Copiez le code suivant dans ce fichier.

 import * as shell depuis "shelljs";

// Copier tous les modèles de vue
shell.cp ("-R", "src / vues", "dist /");

Mise à jour des scripts npm

Mettez à jour les scripts dans package.json avec le code suivant.

 "scripts": {
    "clean": "rimraf dist / *",
    "copy-assets": "outils ts-node / copyAssets",
    "peluche": "tslint -c tslint.json -p tsconfig.json --fix",
    "tsc": "tsc",
    "build": "npm-run-all clean lint tsc copy-assets",
    "dev: start": "npm-run-all build start",
    "dev": "nodemon --watch src -e ts, ejs --exec npm lancer dev: start",
    "start": "node.",
    "test": "echo " Erreur: aucun test spécifié  "&& exit 1"
  },

Remarque: Si vous ne connaissez pas bien les scripts npm ils peuvent être très puissants et utiles pour tout projet Node.js. Les scripts peuvent être enchaînés de plusieurs manières. Une façon de chaîner les scripts consiste à utiliser les préfixes pre et post . Par exemple, si vous avez un script intitulé start et un autre intitulé prestart l'exécution de npm run start sur le terminal sera exécutée pour la première fois prestart et seulement commence à fonctionner.

Lancez maintenant l'application et naviguez jusqu'à http: // localhost: 8080.

 npm run dev

 Page d'accueil de l'inventaire des guitares

La page d'accueil commence à être plus belle! Bien entendu, le bouton Get Started conduit à un message d'erreur décevant. Pas de soucis!

Un meilleur moyen de gérer les paramètres de configuration dans Node.js

Les applications Node.js utilisent généralement des variables d’environnement pour la configuration. Cependant, la gestion des variables d'environnement peut être une corvée. dotenv .

Installez dotenv en tant que dépendance de projet.

 npm install dotenv
npm installer --save-dev @ types / dotenv

Créez un fichier nommé .env dans le dossier racine du projet et ajoutez le code suivant:

 # Défini en production lors du déploiement en production.
NODE_ENV = développement

Configuration du serveur # Node.js
SERVER_PORT = 8080

Remarque: Lorsque vous utilisez un système de contrôle de code source tel que git n'ajoutez pas le fichier .env au contrôle de source. Chaque environnement nécessite un fichier personnalisé .env . Il est recommandé de documenter les valeurs attendues dans le fichier .env du projet README ou dans un fichier séparé .env.sample .

Maintenant, mettez à jour src / index .ts pour utiliser dotenv pour configurer la valeur du port du serveur d’application.

 import dotenv de "dotenv";
importer express de "express";
importer le chemin de "chemin";

// initialise la configuration
dotenv.config ();

// le port est maintenant disponible pour l'exécution de Node.js
// comme s'il s'agissait d'une variable d'environnement
const port = process.env.SERVER_PORT;

const app = express ();

// Configurer Express pour utiliser EJS
app.set ("vues", chemin.join (__dirname, "vues"));
app.set ("moteur de vue", "ejs");

// définir un gestionnaire de route pour la page d'accueil par défaut
app.get ("/", (req, res) => {
    // rendre le modèle d'index
    res.render ("index");
});

// démarre le serveur express
app.listen (port, () => {
    // tslint: disable-next-line: no-console
    console.log (`le serveur a démarré à http: // localhost: $ {port}`);
});

Vous utiliserez .env pour obtenir beaucoup plus d'informations sur la configuration à mesure que le projet se développe.

Ajoutez facilement l'authentification aux nœuds et aux expressions

Ajouter l'enregistrement de l'utilisateur et la connexion (authentification) à toute application est pas une tâche banale. La bonne nouvelle est qu'Okta rend cette étape très facile. Pour commencer, créez un compte développeur gratuit avec Okta. Tout d'abord, accédez à developer.okta.com et cliquez sur le bouton Créer un compte gratuit ou cliquez sur le bouton Inscription .

 un compte gratuit

Après avoir créé votre compte, cliquez sur le lien Applications en haut, puis cliquez sur Ajouter une application .

 Créer une application . ]

Choisissez ensuite une application Web et cliquez sur Suivant .

 Créez une application Web

Entrez un nom pour votre application, telle que comme Inventaire Guitare . Vérifiez que le numéro de port est identique à celui configuré pour votre application Web locale. Cliquez ensuite sur Terminé pour terminer la création de l'application.

 Paramètres de l'application

Copiez-collez le code suivant dans votre fichier .env . [19659114] # Okta configuration
OKTA_ORG_URL = https: // {votreOktaDomain}
OKTA_CLIENT_ID = {votreClientId}
OKTA_CLIENT_SECRET = {votreClientSecret}

Dans la console d’application Okta, cliquez sur l’onglet Général de votre nouvelle application, et trouvez au bas de la page une section intitulée «Informations d’identité du client». Copiez l’ID client puis Valeurs du secret client et collez-les dans votre fichier .env pour remplacer {votreClientId} et {votreClientSecret} respectivement.

 Informations d'identification du client

Activer l'enregistrement en libre-service

L'une des fonctionnalités intéressantes d'Okta permet aux utilisateurs de votre application de créer un compte. Par défaut, cette fonctionnalité est désactivée, mais vous pouvez l'activer facilement. Tout d’abord, cliquez sur le menu Utilisateurs et sélectionnez Enregistrement .

 Enregistrement d’utilisateur

  1. Cliquez sur le bouton Edit .
  2. Modifier Enregistrement en libre service à Activé .
  3. Cliquez sur le bouton Enregistrer au bas du formulaire.

 Activer l'auto-enregistrement [19659139] Sécurisez votre application Node.js</h3 data-recalc-dims=

La dernière étape pour sécuriser votre application Node.js consiste à configurer Express pour qu'il utilise le middleware Okta OpenId Connect (OIDC) .

 npm install @ okta / session express oidc-middleware
npm install --save-dev @ types / express-session

Ensuite, mettez à jour votre fichier .env pour ajouter une valeur HOST_URL et SESSION_SECRET . Vous pouvez remplacer la valeur par SESSION_SECRET par la chaîne de votre choix.

 # configuration du serveur Node.js
SERVER_PORT = 8080
HOST_URL = http: // localhost: 8080
SESSION_SECRET = MySuperCoolAndAwesomeSecretForSigningSessionCookies

Créez un dossier sous src nommé middleware . Ajoutez un fichier au dossier src / middleware nommé sessionAuth.ts . Ajoutez le code suivant à src / middleware / sessionAuth.ts .

 import {ExpressOIDC} à partir de "@ okta / oidc-middleware";
importer une session depuis "express-session";

exportation const register = (app: any) => {
    // Créer le client OIDC
    const oidc = new ExpressOIDC ({
        client_id: process.env.OKTA_CLIENT_ID,
        client_secret: process.env.OKTA_CLIENT_SECRET,
        émetteur: `$ {process.env.OKTA_ORG_URL} / oauth2 / default`,
        redirect_uri: `$ {process.env.HOST_URL} / code d'autorisation / callback`,
        scope: "profil ouvert"
    });

    // Configurer Express pour utiliser des sessions d'authentification
    app.use (session ({
        resave: true,
        saveUninitialized: false,
        secret: process.env.SESSION_SECRET
    }));

    // Configurer Express pour utiliser le routeur client OIDC
    app.use (oidc.router);

    // ajoute le client OIDC à app.locals
    app.locals.oidc = oidc;
};

À ce stade, si vous utilisez un éditeur de code tel que VS Code, vous pouvez voir TypeScript se plaindre du module @ okta / oidc-middleware . Au moment d'écrire ces lignes, ce module n'a pas encore de fichier de déclaration TypeScript officiel. Pour l'instant, créez un fichier dans le dossier src nommé global.d.ts et ajoutez le code suivant:

 déclarez le module "@ okta / oidc-middleware";

Itinéraires de refactor

Au fur et à mesure que l'application grandit, vous ajoutez de nombreux autres itinéraires. C'est une bonne idée de définir tous les itinéraires dans une zone du projet. Créez un nouveau dossier sous src nommé routes . Ajouter un nouveau fichier à src / routes nommé index.ts . Ajoutez ensuite le code suivant à ce nouveau fichier:

 import * as express de "express";

exportation const register = (app: express.Application) => {
    const oidc = app.locals.oidc;

    // définir un gestionnaire de route pour la page d'accueil par défaut
    app.get ("/", (req: any, res) => {
        res.render ("index");
    });

    // définit un gestionnaire d'itinéraire sécurisé pour la page de connexion qui redirige vers / guitars
    app.get ("/ login", oidc.ensureAuthenticated (), (req, res) => {
        res.redirect ("/ guitars");
    });

    // définir un itinéraire pour gérer la déconnexion
    app.get ("/ logout", (req: any, res) => {
        req.logout ();
        res.redirect ("/");
    });

    // définir un gestionnaire d'itinéraire sécurisé pour la page guitars
    app.get ("/ guitars", oidc.ensureAuthenticated (), (req: any, res) => {
        res.render ("guitares");
    });
};

Ensuite, mettez à jour les modules src / index.ts pour utiliser les modules sessionAuth et que vous avez créés.

 import dotenv de "dotenv";
importer express de "express";
importer le chemin de "chemin";
importer * en tant que sessionAuth de "./middleware/sessionAuth";
importer * en tant que routes à partir de "./routes";

// initialise la configuration
dotenv.config ();

// le port est maintenant disponible pour l'exécution de Node.js
// comme s'il s'agissait d'une variable d'environnement
const port = process.env.SERVER_PORT;

const app = express ();

// Configurer Express pour utiliser EJS
app.set ("vues", chemin.join (__dirname, "vues"));
app.set ("moteur de vue", "ejs");

// Configurer l'authentification de session
sessionAuth.register (application);

// Configurer les itinéraires
routes.register (app);

// démarre le serveur express
app.listen (port, () => {
    // tslint: disable-next-line: no-console
    console.log (`le serveur a démarré à http: // localhost: $ {port}`);
});

Créez ensuite un nouveau fichier pour le modèle d'affichage de liste de guitares à partir de src / views / guitars.ejs et entrez le code HTML suivant:




    
    
     Inventaire Guitar 
    
    
    


    

Inventaire Guitar

Votre avenir liste des guitares!

    

Enfin, lancez l'application.

 npm run dev

Remarque: pour vérifier que l'authentification fonctionne correctement, ouvrez un nouveau navigateur ou utilisez une fenêtre de navigateur privé / incognito.

Cliquez sur le bouton Get Started . Si tout se passe bien, connectez-vous avec votre compte Okta et Okta devrait automatiquement vous rediriger vers la page «Liste des guitares»!

 Connexion Okta

L'authentification fonctionne avec le profil de l'utilisateur. informations renvoyées par Okta. Le middleware OIDC associe automatiquement un objet userContext et une fonction isAuthenticated () à chaque requête. Ce userContext a une propriété userinfo qui contient des informations qui ressemblent à l'objet suivant:

 {
  sous: '00abc12defg3hij4k5l6',
  nommer le premier Dernier',
  locale: 'en-US',
  nom_utilisateur_principal: 'account@company.com',
  prenom_nom: 'premier',
  nom_famille: 'dernier',
  zoneinfo: 'America / Los_Angeles',
  updated_at: 1539283620
}

La première étape consiste à récupérer l'objet de profil utilisateur et à le transmettre aux vues sous forme de données. Mettez à jour le src / routes / index.ts avec le code suivant:

 import * comme express à partir de "express";

exportation const register = (app: express.Application) => {
    const oidc = app.locals.oidc;

    // définir un gestionnaire de route pour la page d'accueil par défaut
    app.get ("/", (req: any, res) => {
        const utilisateur = req.userContext? req.userContext.userinfo: null;
        res.render ("index", {isAuthenticated: req.isAuthenticated (), user});
    });

    // définit un gestionnaire d'itinéraire sécurisé pour la page de connexion qui redirige vers / guitars
    app.get ("/ login", oidc.ensureAuthenticated (), (req, res) => {
        res.redirect ("/ guitars");
    });

    // définir un itinéraire pour gérer la déconnexion
    app.get ("/ logout", (req: any, res) => {
        req.logout ();
        res.redirect ("/");
    });

    // définir un gestionnaire d'itinéraire sécurisé pour la page guitars
    app.get ("/ guitars", oidc.ensureAuthenticated (), (req: any, res) => {
        const utilisateur = req.userContext? req.userContext.userinfo: null;
        res.render ("guitars", {isAuthenticated: req.isAuthenticated (), user});
    });
};

Créez un nouveau dossier sous src / views nommé partiels . Créez un nouveau fichier dans ce dossier nommé nav.ejs . Ajoutez le code suivant à src / views / partials / nav.ejs .


Modifiez les fichiers src / views / index.ejs et src / views / guitars.ejs . Immédiatement après l'étiquette insérez le code suivant.

  
    <% include partials/nav %>

Grâce à ces changements, votre application a maintenant un menu de navigation en haut qui change en fonction du statut de connexion de l'utilisateur.

 Navigation

Créez une API avec Node et PostgreSQL

L'étape suivante consiste à ajouter l'API à l'application Guitar Inventory. Cependant, avant de poursuivre, vous avez besoin d'un moyen de stocker des données.

Créer une base de données PostgreSQL

Ce tutoriel utilise PostgreSQL . Pour faciliter les choses, utilisez Docker pour configurer une instance de PostgreSQL. Si Docker n'est pas déjà installé, vous pouvez suivre le guide d'installation .

Une fois Docker installé, exécutez la commande suivante pour télécharger le dernier conteneur PostgreSQL.

 docker pull postgres: dernier

Exécutez maintenant cette commande pour créer une instance d'un serveur de base de données PostgreSQL. N'hésitez pas à modifier la valeur du mot de passe de l'administrateur.

 docker run -d --name guitar-db -p 5432: 5432 -e 'POSTGRES_PASSWORD = p @ ssw0rd42' postgres

Remarque: Si PostgreSQL est déjà installé localement, vous devrez modifier le paramètre -p pour mapper le port 5432 sur un autre port n'entrant pas en conflit avec votre instance existante de PostgreSQL.

Voici une brève explication des paramètres précédents de Docker.

  • -d – Le conteneur est lancé en mode démon, de sorte qu'il s'exécute en arrière-plan.
  • -name – Vous donnez Le conteneur Docker est un nom convivial, utile pour arrêter et démarrer des conteneurs.
  • -p – Ceci mappe le port 5432 de l'hôte (votre ordinateur) sur celui de son conteneur 5432. PostgreSQL, par défaut, écoute. connexions sur le port TCP 5432.
  • -e – Ceci définit une variable d'environnement dans le conteneur. Dans cet exemple, le mot de passe de l'administrateur est p @ ssw0rd42 . Vous pouvez remplacer cette valeur par n'importe quel mot de passe.
  • postgres – Ce dernier paramètre indique à Docker d'utiliser l'image postgres.

Remarque: Si vous redémarrez votre ordinateur, vous devrez peut-être le redémarrer. récipient. Vous pouvez le faire en utilisant la commande du menu fixe start guitar-db .

Installez le module client PostgreSQL et entrez les déclarations à l'aide des commandes suivantes:

 npm install pg pg-promise
npm installer --save-dev @ types / pg

Paramètres de configuration de la base de données

Ajoutez les paramètres suivants à la fin du fichier .env .

 # Configuration de Postgres
PGHOST = localhost
PGUSER = postgres
PGDATABASE = postgres
PGPASSWORD = p @ ssw0rd42
PGPORT = 5432

Remarque: Si vous avez modifié le mot de passe de l'administrateur de la base de données, veillez à remplacer le mot de passe par défaut p @ ssw0rd42 par ce mot de passe dans ce fichier.

Ajouter un script de génération de base de données

Vous besoin d’un script de compilation pour initialiser la base de données PostgreSQL. Ce script doit lire un fichier .pgsql et exécuter les commandes SQL sur la base de données locale

Dans le dossier tools créez deux fichiers: initdb.ts et initdb.pgsql . Copiez et collez le code suivant dans initdb.ts .

 import dotenv de "dotenv";
importer des fs de "fs-extra";
importer {Client} de "pg";

const init = async () => {
    // lire les variables d'environnement
    dotenv.config ();
    // crée une instance du client PostgreSQL
    const client = new Client ();
    essayer {
        // se connecter au serveur de base de données local
        wait client.connect ();
        // lit le contenu du fichier initdb.pgsql
        const sql = wait fs.readFile ("./tools/initdb.pgsql", {encoding: "UTF-8"});
        // divise le fichier en déclarations séparées
        instructions const = sql.split (/;  s * $ / m);
        pour (const statement of statement) {
            if (statement.length> 3) {
                // exécute chacune des instructions
                wait client.query (statement);
            }
        }
    } catch (err) {
        console.log( err );
        throw err;
    } finally {
        // close the database client
        await client.end();
    }
};

init().then( () => {
    console.log( "finished" );
} ).catch( () => {
    console.log( "finished with errors" );
} );

Next, copy and paste the following code into initdb.pgsql.

-- Drops guitars table
DROP TABLE IF EXISTS guitars;

-- Creates guitars table
CREATE TABLE IF NOT EXISTS guitars (
    id INT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
    , user_id varchar(50) NOT NULL
    , brand varchar(50) NOT NULL
    , model varchar(50) NOT NULL
    , year smallint NULL 
    , color varchar(50) NULL
)

Next, add a new script to package.json.

    "initdb": "ts-node tools/initdb",

Now, go to the terminal and run the new script.

npm run initdb

You should see the message finished at the console. A new table named guitars is now in your database! Any time you want to reset your database, just rerun the script.

Add API routes in Node.js

To complete the API, you need to add new routes to Express to create, query, update, and delete guitars. First, create a new file under src/routes named api.ts. Add the following code to this file.

import * as express from "express";
import pgPromise from "pg-promise";

export const register = ( app: express.Application ) => {
    const oidc = app.locals.oidc;
    const port = parseInt( process.env.PGPORT || "5432", 10 );
    const config = {
        database: process.env.PGDATABASE || "postgres",
        host: process.env.PGHOST || "localhost",
        port,
        user: process.env.PGUSER || "postgres"
    };

    const pgp = pgPromise();
    const db = pgp( config );

    app.get( `/api/guitars/all`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const guitars = await db.any( `
                SELECT
                    identifiant
                    , brand
                    , model
                    , year
                    , color
                FROM    guitars
                WHERE   user_id = $[userId]
                ORDER BY year, brand, model`, { userId } );
            return res.json( guitars );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );

    app.get( `/api/guitars/total`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const total = await db.one( `
            SELECT  count(*) AS total
            FROM    guitars
            WHERE   user_id = $[userId]`, { userId }, ( data: { total: number } ) => {
                return {
                    total: +data.total
                };
            } );
            return res.json( total );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );

    app.get( `/api/guitars/find/:search`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const guitars = await db.any( `
                SELECT
                    identifiant
                    , brand
                    , model
                    , year
                    , color
                FROM    guitars
                WHERE   user_id = $[userId]
                AND   ( brand ILIKE $[search] OR model ILIKE $[search] )`,
                { userId, search: `%${ req.params.search }%` } );
            return res.json( guitars );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );

    app.post( `/api/guitars/add`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const id = await db.one( `
                INSERT INTO guitars( user_id, brand, model, year, color )
                VALUES( $[userId]$[brand]$[model]$[year]$[color] )
                RETURNING id;`,
                { userId, ...req.body  } );
            return res.json( { id } );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );

    app.post( `/api/guitars/update`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const id = await db.one( `
                UPDATE guitars
                SET brand = $[brand]
                    , model = $[model]
                    , year = $[year]
                    , color = $[color]
                WHERE
                    id = $[id]
                    AND user_id = $[userId]
                RETURNING
                    id;`,
                { userId, ...req.body  } );
            return res.json( { id } );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );

    app.delete( `/api/guitars/remove/:id`, oidc.ensureAuthenticated(), async ( req: any, res ) => {
        essayer {
            const userId = req.userContext.userinfo.sub;
            const id = await db.result( `
                EFFACER
                FROM    guitars
                WHERE   user_id = $[userId]
                AND     id = $[id]`,
                { userId, id: req.params.id  }, ( r ) => r.rowCount );
            return res.json( { id } );
        } catch ( err ) {
            // tslint:disable-next-line:no-console
            console.error(err);
            res.json( { error: err.message || err } );
        }
    } );
};

Update src/routes/index.ts to include the new api module.

import * as express from "express";
import * as api from "./api";

export const register = ( app: express.Application ) => {
    const oidc = app.locals.oidc;

    // define a route handler for the default home page
    app.get( "/", ( req: any, res ) => {
        const user = req.userContext ? req.userContext.userinfo : null;
        res.render( "index", { isAuthenticated: req.isAuthenticated(), user } );
    } );

    // define a secure route handler for the login page that redirects to /guitars
    app.get( "/login", oidc.ensureAuthenticated(), ( req, res ) => {
        res.redirect( "/guitars" );
    } );

    // define a route to handle logout
    app.get( "/logout", ( req: any, res ) => {
        req.logout();
        res.redirect( "/" );
    } );

    // define a secure route handler for the guitars page
    app.get( "/guitars", oidc.ensureAuthenticated(), ( req: any, res ) => {
        const user = req.userContext ? req.userContext.userinfo : null;
        res.render( "guitars", { isAuthenticated: req.isAuthenticated(), user } );
    } );

    api.register( app );
};

Finally, update src/index.ts to add a new configuration option immediately following the line to create the Express application. This code enables Express to parse incoming JSON data.

const app = express();

// Configure Express to parse incoming JSON data
app.use( express.json() );

Update the User Interface with Vue, Axios, and Parcel

The API is ready. To complete the application, you need to add some code to the frontend to consume the API. You can take advantage of TypeScript with frontend code, as well.

This final step of the project uses Vue for frontend rendering, Axios for making HTTP calls to the backend API, and Parcel to both transpile TypeScript and bundle all the dependencies together into a single JavaScript file.

First, install new dependencies at the console using the following commands.

npm install axios vue materialize-css
npm install --save-dev parcel-bundler @types/axios @types/materialize-css @types/vue

Make a new folder under src named public. Make a new folder under src/public named js. Create a file under src/public/js named main.ts and add the following code.

import axios from "axios";
import * as M from "materialize-css";
import Vue from "vue";

// tslint:disable-next-line no-unused-expression
new Vue( {
    computed: {
        hazGuitars(): boolean {
            return this.isLoading === false && this.guitars.length > 0;
        },
        noGuitars(): boolean {
            return this.isLoading === false && this.guitars.length === 0;
        }
    },
    data() {
        return {
            brand: "",
            color: "",
            guitars: [],
            isLoading: true,
            model: "",
            selectedGuitar: "",
            selectedGuitarId: 0,
            year: ""
        };
    },
    el: "#app",
    methods: {
        addGuitar() {
            const guitar = {
                brand: this.brand,
                color: this.color,
                model: this.model,
                year: this.year
            };
            axios
                .post( "/api/guitars/add", guitar )
                .then( () => {
                    this.$refs.year.focus();
                    this.brand = "";
                    this.color = "";
                    this.model = "";
                    this.year = "";
                    this.loadGuitars();
                } )
                .catch( ( err: any ) => {
                    // tslint:disable-next-line:no-console
                    console.log( err );
                } );
        },
        confirmDeleteGuitar( id: string ) {
            const guitar = this.guitars.find( ( g ) => g.id === id );
            this.selectedGuitar = `${ guitar.year } ${ guitar.brand } ${ guitar.model }`;
            this.selectedGuitarId = guitar.id;
            const dc = this.$refs.deleteConfirm;
            const modal = M.Modal.init( dc );
            modal.open();
        },
        deleteGuitar( id: string ) {
            axios
                .delete( `/api/guitars/remove/${ id }` )
                .then( this.loadGuitars )
                .catch( ( err: any ) => {
                    // tslint:disable-next-line:no-console
                    console.log( err );
                } );
        },
        loadGuitars() {
            axios
                .get( "/api/guitars/all" )
                .then( ( res: any ) => {
                    this.isLoading = false;
                    this.guitars = res.data;
                } )
                .catch( ( err: any ) => {
                    // tslint:disable-next-line:no-console
                    console.log( err );
                } );
        }
    },
    mounted() {
        return this.loadGuitars();
    }
} );

Update tsconfig.json to exclude the src/public folder from the backend Node.js build process.

{
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "target": "es6",
        "noImplicitAny": true,
        "moduleResolution": "node",
        "sourceMap": true,
        "outDir": "dist",
        "baseUrl": ".",
        "paths": {
            "*": [
                "node_modules/*"
            ]
        }
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "src/public"
    ]
}

Create a new tsconfig.json file under src/public/js and add the following code. This TypeScript configuration is to compile main.ts for use in the browser.

{
    "compilerOptions": {
        "lib": [
            "es6",
            "dom"
        ],
        "noImplicitAny": true,
        "allowJs": true,
        "target": "es5",
        "strict": true,
        "module": "es6",
        "moduleResolution": "node",
        "outDir": "../../../dist/public/js",
        "sourceMap": true
    }
}

Next, update src/index.ts to configure Express to serve static files from the public folder. Add this line after the code that configures Express to use EJS.

...
// Configure Express to use EJS
app.set( "views", path.join( __dirname, "views" ) );
app.set( "view engine", "ejs" );

// Configure Express to serve static files in the public folder
app.use( express.static( path.join( __dirname, "public" ) ) );

Update src/views/guitars.ejs to add the Vue application template and a reference to the js/main.js file.




    
    
    Guitar Inventory
    
    
    


    <% include partials/nav %>
    

Guitar list

                                                                                                                                                                                                                                            
Year Brand Model Color
                                

No guitars yet!

Add a guitar

        
    
    %MINIFYHTML744903a3114d8f3d2708d5715a92557810%

Finally, update package.json to add a new parcel script, update the build script, and add a new alias section for Vue. The alias section points Parcel to the correct Vue file to bundle with src/public/js/main.ts.

  "scripts": {
    "clean": "rimraf dist/*",
    "copy-assets": "ts-node tools/copyAssets",
    "lint": "tslint -c tslint.json -p tsconfig.json --fix",
    "tsc": "tsc",
    "parcel": "parcel build src/public/js/main.ts -d dist/public/js",
    "build": "npm-run-all clean lint tsc copy-assets parcel",
    "dev:start": "npm-run-all build start",
    "dev": "nodemon --watch src -e ts,ejs --exec npm run dev:start",
    "start": "node .",
    "initdb": "ts-node tools/initdb",
    "test": "echo "Error: no test specified" && exit 1"
  },
  "alias": {
    "vue": "./node_modules/vue/dist/vue.common.js"
  },

Now, restart the build and take your new web application for a spin!

npm run dev

Guitar Inventory

Learn More About Node and TypeScript

This tutorial only scratches the surface of what you can do with Node.js and TypeScript. Below are more resources to explore.

You can find the completed Guitar Inventory project on GitHub.

Follow us for more great content and updates from our team! You can find us on TwitterFacebookand LinkedIn. Des questions? Hit us up in the comments below.





Source link