Comment créer un raccourcisseur d'URL de type sécurisé dans NodeJS avec NestJS

De Get Docs
Aller à :navigation, rechercher

Introduction

URL, abréviation de Uniform Resource Locator, est une adresse donnée à une ressource unique sur le Web. Étant donné qu'une URL est unique, deux ressources ne peuvent pas avoir la même URL.

La longueur et la complexité des URL varient. Une URL peut être aussi courte que example.com ou aussi longue que http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk. Les URL complexes peuvent être disgracieuses, causer des problèmes d'optimisation des moteurs de recherche (SEO) et avoir un impact négatif sur les plans marketing. Les raccourcisseurs d'URL mappent une URL longue à une URL plus courte et redirigent l'utilisateur vers l'URL d'origine lorsque l'URL courte est utilisée.

Dans ce didacticiel, vous allez créer un raccourcisseur d'URL à l'aide de NestJS. Tout d'abord, vous implémenterez la logique de raccourcissement et de redirection d'URL dans un service. Ensuite, vous créerez des gestionnaires de route pour faciliter les demandes de raccourcissement et de redirection.

Conditions préalables

Pour suivre ce tutoriel, vous aurez besoin de :

Étape 1 - Préparation de votre environnement de développement

Dans cette étape, vous allez configurer tout ce dont vous avez besoin pour commencer à implémenter votre logique de raccourcissement d'URL. Vous installerez NestJS globalement, générerez un nouveau passe-partout d'application NestJS, installerez des dépendances et créerez le module, le service et le contrôleur de votre projet.

Tout d'abord, vous installerez la CLI Nest globalement si vous ne l'avez pas déjà installée. Vous utiliserez cette CLI pour générer votre répertoire de projet et les fichiers requis. Exécutez la commande suivante pour installer la CLI Nest :

npm install -g @nestjs/cli

La -g flag installera la CLI Nest globalement sur votre système.

Vous verrez la sortie suivante :

Output...
added 249 packages, and audited 250 packages in 3m
39 packages are looking for funding
run npm fund for details
found 0 vulnerabilities

Ensuite, vous utiliserez le new commande pour créer le projet et générer les fichiers de démarrage standard nécessaires :

nest new URL-shortener

Vous verrez la sortie suivante :

Output...
⚡  We will scaffold your app in a few seconds..

CREATE url-shortener/.eslintrc.js (631 bytes)
CREATE url-shortener/.prettierrc (51 bytes)
CREATE url-shortener/nest-cli.json (118 bytes)
CREATE url-shortener/package.json (2002 bytes)
CREATE url-shortener/README.md (3339 bytes)
CREATE url-shortener/tsconfig.build.json (97 bytes)
CREATE url-shortener/tsconfig.json (546 bytes)
CREATE url-shortener/src/app.controller.spec.ts (617 bytes)
CREATE url-shortener/src/app.controller.ts (274 bytes)
CREATE url-shortener/src/app.module.ts (249 bytes)
CREATE url-shortener/src/app.service.ts (142 bytes)
CREATE url-shortener/src/main.ts (208 bytes)
CREATE url-shortener/test/app.e2e-spec.ts (630 bytes)
CREATE url-shortener/test/jest-e2e.json (183 bytes)

? Which package manager would you ❤️  to use? (Use arrow keys)
> npm
  yarn
  pnpm

Choisir npm.

Vous verrez le résultat suivant :

Output√ Installation in progress... ☕

🚀  Successfully created project url-shortener
👉  Get started with the following commands:

$ cd url-shortener
$ npm run start

                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.

               🍷  Donate: https://opencollective.com/nest

Accédez au répertoire de votre projet créé :

cd url-shortener

Vous exécuterez toutes les commandes suivantes dans ce répertoire.

Remarque : La CLI NestJS crée app.controller.ts, app.controller.spec.ts, et app.service.ts fichiers lorsque vous générez un nouveau projet. Comme vous n'en aurez pas besoin dans ce didacticiel, vous pouvez soit les supprimer, soit les ignorer.


Ensuite, vous installerez les dépendances requises.

Ce didacticiel nécessite quelques dépendances, que vous installerez à l'aide du gestionnaire de packages par défaut de NodeJS npm. Les dépendances requises incluent TypeORM, SQLite, Class-validator, Class-transformer et Nano-ID .

TypeORM est un mappeur objet-relationnel qui facilite les interactions entre une application TypeScript et une base de données relationnelle. Cet ORM fonctionne de manière transparente avec NestJS grâce à NestJS dédié @nestjs/typeorm forfait. Vous utiliserez cette dépendance avec le natif de NestJS typeorm package pour interagir avec une base de données SQLite.

Exécutez la commande suivante pour installer TypeORM et son package NestJS dédié :

npm install @nestjs/typeorm typeorm

SQLite est une bibliothèque qui implémente un petit moteur de base de données SQL rapide et autonome. Vous utiliserez cette dépendance comme base de données pour stocker et récupérer des URL raccourcies.

Exécutez la commande suivante pour installer SQLite :

npm install sqlite3

Le package class-validator contient des décorateurs utilisés pour la validation des données dans NestJS. Vous utiliserez cette dépendance avec votre objet de transfert de données pour valider les données envoyées dans votre application.

Exécutez la commande suivante pour installer class-validator:

npm install class-validator

Le package class-transformer vous permet de transformer des objets simples en une instance d'une classe et vice-versa. Vous utiliserez cette dépendance avec le class-validator car il ne peut pas fonctionner seul.

Exécutez la commande suivante pour installer class-transformer:

npm install class-transformer

Nano-ID est un générateur d'ID de chaîne unique sécurisé et convivial pour les URL. Vous utiliserez cette dépendance pour générer un identifiant unique pour chaque ressource URL.

Exécutez la commande suivante pour installer Nano-ID :

npm install [email protected]^3.0.0

Remarque : Versions de Nano-ID supérieures à 3.0.0 support désactivé pour les modules CommonJS. Ce problème peut provoquer une erreur dans votre application car le code JavaScript produit par le compilateur TypeScript utilise toujours le système de modules CommonJS.


Après avoir installé les dépendances requises, vous générerez le module, le service et le contrôleur du projet à l'aide de la CLI Nest. Le module organisera votre projet, le service gérera toute la logique du raccourcisseur d'URL et le contrôleur gérera les routes.

Exécutez la commande suivante pour générer votre module :

nest generate module url

Vous verrez la sortie suivante :

OutputCREATE src/url/url.module.ts (80 bytes)
UPDATE src/app.module.ts (304 bytes)

Ensuite, exécutez la commande suivante pour générer votre service :

nest generate service url --no-spec

La --no-spec indique à la CLI Nest de générer les fichiers sans leurs fichiers de test. Vous n'aurez pas besoin des fichiers de test dans ce didacticiel.

Vous verrez la sortie suivante :

OutputCREATE src/url/url.service.ts (87 bytes)
UPDATE src/url/url.module.ts (151 bytes)

Exécutez ensuite la commande suivante pour générer votre contrôleur :

nest generate controller url --no-spec

Vous verrez la sortie suivante :

OutputCREATE src/url/url.controller.ts (95 bytes)
UPDATE src/url/url.module.ts (233 bytes)

Dans cette étape, vous avez généré votre application et la plupart des fichiers nécessaires à votre développement. Ensuite, vous allez connecter votre application à une base de données.

Étape 2 - Connexion de votre application à une base de données

Dans cette étape, vous allez créer une entité pour modéliser la ressource URL dans votre base de données. Une entité est un fichier contenant les propriétés nécessaires des données stockées. Vous allez également créer un référentiel en tant que couche d'accès entre votre application et sa base de données.

Utilisant nano ou votre éditeur de texte préféré, créez et ouvrez un fichier dans le src/url dossier appelé url.entity.ts:

nano src/url/url.entity.ts

Ce fichier contiendra l'entité pour modéliser vos données.

Ensuite, dans votre src/url/url.entity.ts fichier, ajoutez le code Typescript suivant :

src/url/url.entity.ts

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Url {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    urlCode: string;

    @Column()
    longUrl: string;

    @Column()
    shortUrl: string;
}

Tout d'abord, vous importez le Entity, Column, et PrimaryGeneratedColumn décorateurs de 'typeorm'.

Le code crée et exporte une classe Url annoté avec le Entity décorateur qui marque une classe comme une entité.

Chaque propriété est spécifiée et annotée avec les décorateurs appropriés : PrimaryGeneratedColumn pour l'identifiant et Column pour le rest des propriétés. PrimaryGeneratedColumn est un décorateur qui génère automatiquement une valeur pour les propriétés qu'il annote. TypeOrm l'utilisera pour générer un identifiant pour chaque ressource. Column est un décorateur qui ajoute une propriété qu'il annote en tant que colonne dans une base de données.

Les propriétés qui doivent être stockées dans la base de données incluent les éléments suivants :

  • id est la clé primaire de la table de la base de données.
  • urlCode est l'identifiant unique généré par le nanoid package et sera utilisé pour identifier chaque URL.
  • longUrl est l'URL envoyée à votre application pour être raccourcie.
  • shortUrl est l'URL raccourcie.

Enregistrez et fermez le fichier.

Ensuite, vous allez créer une connexion entre votre application et votre base de données.

Tout d'abord, ouvrez src/app.module.ts dans nano ou votre éditeur de texte préféré :

nano src/app.module.ts

Ensuite, ajoutez les lignes en surbrillance au fichier :

src/app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url/url.entity';
import { UrlModule } from './url/url.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'URL.sqlite',
      entities: [Url],
      synchronize: true,
    }),
    UrlModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

Tout d'abord, importez TypeOrmModule de @nestjs/typeorm et Url de ./url/url.entity. Vous pouvez encore avoir des lignes liées à AppController et AppService. Les laisser dans le fichier n'affectera pas le rest du didacticiel.

Dans le tableau des importations, appelez le forRoot méthode sur la TypeOrmModule pour partager la connexion à travers tous les modules de votre application. La forRoot La méthode prend un objet de configuration comme argument.

L'objet de configuration contient des propriétés qui créent la connexion. Ces propriétés incluent les éléments suivants :

  • La type La propriété indique le type de base de données avec laquelle vous utilisez TypeOrm pour interagir. Dans ce cas, il est réglé sur 'sqlite'.
  • La database La propriété indique le nom préféré pour votre base de données. Dans ce cas, il est réglé sur 'URL.sqlite'.
  • La entities La propriété est un tableau de toutes les entités de votre projet. Dans ce cas, vous avez une seule entité spécifiée comme Url à l'intérieur du tableau.
  • La synchronize L'option synchronise automatiquement vos tables de base de données avec votre entité et met à jour les tables chaque fois que vous exécutez le code. Dans ce cas, il est réglé sur true.

Remarque : Réglage synchronize à true n'est idéal que dans un environnement de développement. Il doit toujours être réglé sur false en production, car cela pourrait entraîner une perte de données.


Enregistrez et fermez le fichier.

Ensuite, vous allez créer un référentiel pour servir de couche d'accès entre votre application et votre base de données. Vous devrez connecter votre entité à son module parent, et cette connexion permet à Nest et TypeOrm de créer automatiquement un référentiel.

Ouvert src/url/url.module.ts:

nano src/url/url.module.ts

Dans le fichier existant, ajoutez les lignes en surbrillance :

src/url/url.module.ts

import { Module } from '@nestjs/common';
import { UrlService } from './url.service';
import { UrlController } from './url.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Url])],
  providers: [UrlService],
  controllers: [UrlController],
})
export class UrlModule {}

Vous créez un imports tableau à l'intérieur du Module décorateur où vous importez TypeOrmModule de @nestjs/typeorm et Url de ./url.entity. À l'intérieur de imports tableau, vous appelez le forFeature méthode sur la TypeOrmModule. La forFeature La méthode prend un tableau d'entités comme argument, donc vous passez dans le Url entité.

Enregistrez et fermez le fichier.

Nest et TypeOrm créeront un référentiel dans les coulisses qui agira comme une couche d'accès entre votre service et la base de données.

Dans cette étape, vous avez connecté votre application à une base de données. Vous êtes maintenant prêt à implémenter la logique de raccourcissement d'URL.

Étape 3 - Implémentation de la logique de service

Dans cette étape, vous implémenterez votre logique de service avec deux méthodes. La première méthode, shortenUrl, contiendra toute la logique de raccourcissement d'URL. La deuxième méthode, redirect, contiendra toute la logique pour rediriger un utilisateur vers l'URL d'origine. Vous allez également créer un objet de transfert de données pour valider les données entrant dans votre application.

Fournir un accès de service au référentiel

Avant d'implémenter ces méthodes, vous donnerez à votre service l'accès à votre référentiel pour permettre à votre application de lire et d'écrire des données dans la base de données.

Tout d'abord, ouvrez src/url/url.service.ts:

nano src/url/url.service.ts

Ajoutez les lignes en surbrillance suivantes au fichier existant :

src/url/url.service.ts

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}
}

Vous importez Repository de typeorm, InjectRepository de @nestjs/typeorm, et Url de ./url.entity.

Dans ton UrlService classe, vous créez une constructor. À l'intérieur de constructor, vous déclarez une variable privée, repo, comme paramètre. Ensuite, vous affectez un type de Repository à repo avec un type générique de Url. Vous annotez le repo variables avec le InjectRepository décorateur et pass Url comme argument.

Enregistrez et fermez le fichier.

Votre service a maintenant accès à votre référentiel via le repo variable. Toutes les requêtes de base de données et les méthodes TypeOrm y seront appelées.

Ensuite, vous allez créer une méthode asynchrone, shortenUrl. La méthode prendra une URL comme argument et renverra une URL raccourcie. Pour valider que les données introduites dans la méthode sont valides, vous utiliserez un objet de transfert de données avec le class-validator et class-transformer paquets pour valider les données.

Création d'un objet de transfert de données

Avant de créer la méthode asynchrone, vous allez créer l'objet de transfert de données requis par le shortenUrl méthode asynchrone. Un objet de transfert de données est un objet qui définit comment les données seront envoyées entre les applications.

Tout d'abord, créez un dtos (objets de transfert de données) dans votre dossier url dossier:

mkdir src/url/dtos

Ensuite, créez un fichier nommé url.dto.ts dans ce dossier :

nano src/url/dtos/url.dto.ts

Ajoutez le code suivant au nouveau fichier :

src/url/dto/url.dto.ts

import { IsString, IsNotEmpty } from 'class-validator';

export class ShortenURLDto {
  @IsString()
  @IsNotEmpty()
  longUrl: string;
}

Vous importez le IsString et IsNotEmpty décorateurs de class-validator. Ensuite, vous créez et exportez la classe ShortenURLDto. A l'intérieur de votre ShortenURLDto classe, vous créez une longUrl propriété et affectez-lui un type de string.

Vous annotez également le longUrl propriété avec le IsString et IsNotEmpty décorateurs. Annoter le longUrl propriété avec ces décorateurs assurera que longUrl est toujours une chaîne et n'est pas vide.

Enregistrez et fermez le fichier.

Ensuite, ouvrez votre src/main.ts dossier:

nano src/main.ts

Ajoutez les morceaux de code en surbrillance au fichier existant :

src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(3000);
}
bootstrap();

Vous importez ValidationPipe, qui utilise le class-validator package pour appliquer les règles de validation sur toutes les données entrant dans votre application.

Ensuite, vous appelez le useGlobalPipes méthode sur votre instance d'application (app) et passez une instance de ValidationPipe avec un objet options où le whitelist la propriété est définie sur true. La useGlobalPipes la méthode se lie ValidationPipe au niveau de l'application, garantissant que toutes les routes sont protégées contre les données incorrectes. Réglage de la whitelist propriété à true supprime les objets validés (renvoyés) des propriétés qui ne sont pas spécifiées dans votre DTO.

Enregistrez et fermez le fichier.

Ensuite, vous allez importer votre objet de transfert de données dans le url.service.ts fichier et appliquez-le au shortenUrl méthode.

Créer le shortenUrl Méthode

La shortenUrl gérera la plupart de la logique de raccourcissement d'URL. Il faudra un paramètre url du genre ShortenURLDto.

Tout d'abord, ouvrez votre src/url/url.service.ts dossier:

nano src/url/url.service.ts

Ajoutez les lignes en surbrillance au fichier :

src/url/url.service.ts

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';
...

Tout d'abord, vous importez NotFoundExeception, BadRequestException, et UnprocessableEntityException de @nestjs/common car vous les utiliserez pour la gestion des erreurs. Ensuite, vous importez {nanoid} de nanoid et isURL de class-validator. isURL sera utilisé pour confirmer si le produit fourni longUrl est une URL valide. Enfin, vous importez ShortenURLDto de './dtos/url.dto' pour la validation des données.

Ensuite, ajoutez ce qui suit au UrlService classe en dessous de la constructor:

src/url/url.service.ts

...
async shortenUrl(url: ShortenURLDto) {}

Ensuite, ajoutez le code suivant à votre shortenUrl méthode:

src/url/url.service.ts

...
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }

Dans le bloc de code ci-dessus, le longUrl a été déstructuré de la url objet. Ensuite, en utilisant le isURL méthode, une vérification validera si longUrl est une URL valide.

A urlCode est généré à l'aide nanoid. Par défaut, nanoid génère une chaîne unique de vingt et un caractères. Transmettez la longueur souhaitée comme argument pour remplacer le comportement par défaut. Dans ce cas, vous passez une valeur de 10 parce que vous voulez que l'URL soit aussi courte que possible.

Ensuite, l'URL de base est définie. Une URL de base est la racine cohérente de l'adresse de votre site Web. En développement, c'est votre serveur hôte local ; en production, c'est votre nom de domaine. Pour ce didacticiel, l'exemple de code utilise localhost.

A try-catch block hébergera tout le code pour interagir avec la base de données pour la gestion des erreurs.

Raccourcir une URL deux fois peut conduire à des données en double, donc une requête de recherche est exécutée sur la base de données pour voir si l'URL existe. S'il existe, c'est shortUrl sera retourné ; sinon, le code progresse pour le raccourcir. Si aucun enregistrement d'URL n'est trouvé dans la base de données, une URL courte est créée en concaténant le baseURL et le urlCode.

Puis un url instance d'entité est créée avec le urlCode, la longUrl, et le shortUrl. La url instance est enregistrée dans la base de données en appelant le save méthode sur repo et en passant l'instance comme argument. Puis le shortUrl est retourné.

Enfin, si une erreur se produit, l'erreur est consignée dans la console dans le bloc catch, et un UnprocessableEntityException message sera lancé.

C'est ce que votre url.service.ts le fichier ressemblera maintenant à :

src/url/url.service.ts

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }
}

Enregistrez le fichier.

Ici, vous configurez la première partie de la logique de raccourcissement d'URL. Ensuite, vous implémenterez le redirect méthode à votre service.

Créer le redirect Méthode

La redirect contiendra la logique qui redirige les utilisateurs vers l'URL longue.

Toujours dans le src/url/url/service.ts fichier, ajoutez le code suivant au bas de votre UrlService classe pour mettre en œuvre redirect méthode:

src/url/url.service.ts

...
  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }

La redirect la méthode prend urlCode comme argument et essaie de trouver une ressource dans la base de données avec une correspondance urlCode. Si la ressource existe, il renverra la ressource. Sinon, il jette un NotFoundException Erreur.

Votre terminé url.service.ts le fichier ressemblera maintenant à ceci :

src/url/url.service.ts

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }

  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }
}

Enregistrez et fermez le fichier.

Votre logique de raccourcissement d'URL est maintenant terminée avec deux méthodes : une pour raccourcir l'URL et l'autre pour rediriger l'URL raccourcie vers l'URL d'origine.

Dans l'étape suivante, vous implémenterez le gestionnaire d'itinéraire pour ces deux méthodes dans votre classe de contrôleur.

Étape 4 - Implémentation de la logique du contrôleur

Dans cette étape, vous allez créer deux gestionnaires de routage : un POST gestionnaire d'itinéraire pour gérer les demandes de raccourcissement et un GET gestionnaire de route pour gérer les demandes de redirection.

Avant d'implémenter les routes dans votre classe de contrôleur, vous devez mettre le service à la disposition de votre contrôleur.

Tout d'abord, ouvrez votre src/url/url.controller.ts dossier:

nano src/url/url.controller.ts

Ajoutez les lignes en surbrillance au fichier :

src/url/url.controller.ts

import { Controller } from '@nestjs/common';
import { UrlService } from './url.service';

@Controller('url')
export class UrlController {
  constructor(private service: UrlService) {}
}

Tout d'abord, vous importez UrlService de ./url.service. Ensuite, dans votre classe de contrôleur, vous déclarez un constructeur et initialisez une variable privée, service, comme paramètre. Vous attribuez service un type de UrlService.

La Controller le décorateur a actuellement une chaîne, 'url', en tant qu'argument, ce qui signifie que le contrôleur ne traitera que les requêtes adressées à localhost/3000/url/route. Ce comportement introduit un bogue dans votre logique de raccourcissement d'URL car les URL raccourcies incluent une URL de base (localhost:3000) et un code URL (wyt4_uyP-Il), qui se combinent pour former la nouvelle URL (localhost:3000/wyt4_uyP-Il). Par conséquent, ce contrôleur ne peut pas gérer les requêtes et les liens raccourcis renverront un 404 Not Found Erreur. Pour résoudre ce problème, supprimez le 'url' argument du décorateur Controller et implémentez des itinéraires individuels pour chaque gestionnaire.

Après avoir retiré le 'url' argument, c'est ce que votre UrlController ressemblera:

src/url/url.controller.ts

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}
}

Toujours dans le src/url/url.controller.ts fichier, ajoutez les éléments en surbrillance au import déclaration:

src/url/url.controller.ts

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

Vous importez Body, Get, Param, Post, et Res de @nestjs/common et ShortenURLDto de ./dtos/url.dto. Les décorateurs seront définis plus en détail au fur et à mesure que vous ajouterez à ce fichier.

Ajoutez ensuite les lignes suivantes au UrlController sous le constructor pour définir le POST gestionnaire d'itinéraire :

src/url/url.controller.ts

...
  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

Vous créez une méthode shortenUrl qui prend un argument de url avec un type de ShortenURLDto. vous annotez url avec le Body décorateur pour extraire l'objet corps de l'objet requête et remplir le url variable avec sa valeur.

Vous annotez ensuite toute la méthode avec le Post décorateur et pass 'shorten' comme argument. Ce gestionnaire traitera toutes les demandes de publication faites à localhost:<port>/shorten. Vous appelez alors le shortenUrl méthode sur la service et passer url comme argument.

Ensuite, ajoutez les lignes suivantes sous le POST route pour définir le GET gestionnaire de routage pour la redirection :

src/url/url.controller.ts

...
  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }

Vous créez un redirect méthode avec deux paramètres : res, annoté du Res décorateur et code, annoté du Param décorateur. Le décorateur Res transforme la classe qu'il annote en un objet de réponse Express, vous permettant d'utiliser des commandes spécifiques à la bibliothèque comme Express redirect méthode. La Param décorateur extrait le params propriété de la req object et remplit le paramètre décoré avec sa valeur.

Vous annotez le redirect méthode avec la Get décorateur et passez un paramètre générique, ':code'. Vous passez alors 'code' comme argument à la Param décorateur.

Vous appelez alors le redirect méthode sur service, await le résultat, et le stocker dans une variable, url.

Enfin, tu reviens res.redirect() et passer url.longUrl comme argument. Cette méthode gérera GET demande à localhost:<port>/code, ou vos URL raccourcies.

Ton src/url/url.controller.ts le fichier ressemblera maintenant à ceci :

src/url/url.controller.ts

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}

  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }
}

Enregistrez et fermez le fichier.

Maintenant que vous avez défini votre POST et GET gestionnaires de route, votre raccourcisseur d'URL est entièrement fonctionnel. À l'étape suivante, vous le testerez.

Étape 5 - Test du raccourcisseur d'URL

Dans cette étape, vous testerez le raccourcisseur d'URL que vous avez défini dans les étapes précédentes.

Tout d'abord, démarrez votre application en exécutant :

npm run start

Vous verrez la sortie suivante :

Output[Nest] 12640  - 06/08/2022, 16:20:04     LOG [NestFactory] Starting Nest application...
[Nest] 12640  - 06/08/2022, 16:20:07     LOG [InstanceLoader] AppModule dependencies initialized +2942ms
[Nest] 12640  - 06/08/2022, 16:20:07     LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +257ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [InstanceLoader] TypeOrmModule dependencies initialized +2ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [InstanceLoader] UrlModule dependencies initialized +4ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [RoutesResolver] UrlController {/}: +68ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [RouterExplorer] Mapped {/shorten, POST} route +7ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [RouterExplorer] Mapped {/:code, GET} route +2ms
[Nest] 12640  - 06/08/2022, 16:20:08     LOG [NestApplication] Nest application successfully started +7ms

Ouvrir un nouveau terminal à utiliser curl ou votre outil de test d'API préféré pour faire un POST demande à http://localhost:3000/shorten avec les données ci-dessous ou toute autre donnée de votre choix. Pour plus d'informations sur l'utilisation curl, voir la description dans ce tutoriel.

Exécutez cette commande pour créer un échantillon POST demande:

curl -d "{\"longUrl\":\"http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk\"}" -H "Content-Type: application/json" http://localhost:3000/shorten

La -d drapeau enregistre le HTTP POST demander des données, et le -H flag définit les en-têtes pour le HTTP demande. L'exécution de cette commande enverra l'URL longue à votre application et renverra une URL raccourcie.

Vous recevrez une URL courte en réponse, comme dans l'exemple suivant :

http://localhost:3000/MWBNHDiloW

Enfin, copiez l'URL courte et collez le lien dans votre navigateur. Puis appuyez ENTER. Vous serez redirigé vers la ressource d'origine.

Conclusion

Dans cet article, vous avez créé un raccourcisseur d'URL avec NestJS. Si vous ajoutez une interface frontale, vous pouvez la déployer pour un usage public. Vous pouvez consulter le projet complet sur Github.

NestJS fournit la sécurité de type et architecture pour rendre votre application plus sécurisée, maintenable et évolutive. Pour en savoir plus, consultez la documentation officielle NestJS.