Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🟢
Intermediaire 20 min NestJS

Caching avec NestJS et Redis

Tutoriel : Caching avec NestJS et Redis

Pourquoi Caching avec NestJS et Redis ?

Contexte réel : En tant que développeur backend, vous avez probablement déjà rencontré des problèmes de performance liés à la récupération de données complexes ou à des opérations intensives. Le caching est une technique efficace pour améliorer ces performances en stockant les résultats d'opérations coûteuses dans une mémoire temporaire, permettant ainsi aux futures requêtes identiques de satisfaire rapidement cette mémoire plutôt que de recalculer ou de récupérer les données à nouveau.

Un cas d'utilisation concret : Imaginez une application e-commerce qui affiche des produits en fonction du panier d'un utilisateur. Chaque fois qu'un utilisateur ajoute un produit au panier, la page du panier doit être recalculée pour refléter le changement. Si vous n'utilisez pas de caching, chaque fois que l'utilisateur accède à sa page de panier, votre application devra effectuer des opérations coûteuses comme récupérer les informations sur tous les produits dans son panier depuis la base de données. Avec le caching, vous pouvez stocker ces informations en mémoire pour une certaine durée, évitant ainsi les opérations coûteuses et réduisant considérablement le temps de réponse.

Prérequis

Connaissances nécessaires :

  • Connaissance avancée de NestJS
  • Expérience avec Node.js
  • Familiarité avec Redis

Outils à installer (versions) :

  • Node.js >= 14.0.0
  • npm >= 6.0.0
  • Redis >= 5.0.0

Concepts fondamentaux

1. Caching en général

Le caching est une technique consistant à stocker les résultats d'opérations dans une mémoire temporaire pour des accès rapides futurs. Dans le contexte de NestJS, nous utiliserons Redis comme base de données mémoire.

Schéma mental :

  • Opération coûteuse : L'action qui est trop lente à exécuter sans optimisation.
  • Caching : Stockage temporaire des résultats de ces opérations pour un accès rapide futur.
  • Expiration : Durée durant laquelle les données sont stockées en cache avant d'être supprimées.

Code fonctionnel :

// Importer le module Redis
import * as redis from 'redis';

// Créer une instance de client Redis
const client = redis.createClient();

client.on('error', (err) => {
  console.log('Redis error:', err);
});

client.on('connect', () => {
  console.log('Connected to Redis');
});

2. Intégration avec NestJS

NestJS fournit un module de base pour intégrer le caching, mais nous utiliserons Redis directement.

Schéma mental :

  • Module Cache : Fournit des services pour gérer le caching.
  • Middleware Caching : Permet d'ajouter du caching à vos contrôleurs NestJS.

Code fonctionnel :

// Importer les modules nécessaires
import { Module, MiddlewareConsumer } from '@nestjs/common';
import * as redis from 'redis';

@Module({
  providers: [
    {
      provide: 'REDIS_CLIENT',
      useFactory: () => redis.createClient(),
    },
  ],
})
export class CacheModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CacheMiddleware)
      .forRoutes('*');
  }
}

3. Utilisation du caching

Nous allons utiliser le caching pour stocker les résultats d'une requête à la base de données.

Schéma mental :

  • Récupération des données : L'action qui récupère les données.
  • Stockage en cache : Stocke les données récupérées dans Redis.
  • Récupération depuis le cache : Récupère les données stockées en cache si elles existent.

Code fonctionnel :

// Importer les modules nécessaires
import { Injectable } from '@nestjs/common';
import * as redis from 'redis';

@Injectable()
export class ProductService {
  private client: redis.RedisClient;

  constructor() {
    this.client = redis.createClient();
  }

  async getProductById(id: string): Promise<any> {
    const cachedProduct = await new Promise((resolve, reject) => {
      this.client.get(`product:${id}`, (err, reply) => {
        if (err) return reject(err);
        resolve(reply ? JSON.parse(reply) : null);
      });
    });

    if (cachedProduct) {
      console.log('Retrieved from cache');
      return cachedProduct;
    }

    const product = await this.fetchProductFromDatabase(id); // Simulate database fetch
    this.client.setex(`product:${id}`, 3600, JSON.stringify(product)); // Cache for 1 hour

    return product;
  }

  private async fetchProductFromDatabase(id: string): Promise<any> {
    // Simulate database fetch
    console.log('Fetching from database');
    return { id, name: 'Example Product', price: 19.99 };
  }
}

Mise en pratique : projet fil rouge

Projet fil rouge : Nous allons créer un simple API de blog qui utilise le caching pour stocker les articles.

Structure des fichiers :

src/
├── app.controller.ts
├── app.module.ts
├── blog/
│   ├── blog.controller.ts
│   ├── blog.service.ts
│   └── blog.entity.ts
└── cache/
    └── cache.middleware.ts

1. Configuration de l'application

app.module.ts :

import { Module } from '@nestjs/common';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { CacheModule } from './cache/cache.module';

@Module({
  imports: [
    CacheModule,
    // Other modules
  ],
  controllers: [AppController],
  providers: [
    // Services
  ],
})
export class AppModule {}

2. Middleware de caching

cache.middleware.ts :

import { Injectable, NestMiddleware } from '@nestjs/common';
import * as redis from 'redis';

@Injectable()
export class CacheMiddleware implements NestMiddleware {
  private client: redis.RedisClient;

  constructor() {
    this.client = redis.createClient();
  }

  use(req: any, res: any, next: () => void) {
    const key = req.url;
    this.client.get(key, (err, reply) => {
      if (err) {
        console.error('Redis error:', err);
        return next();
      }

      if (reply !== null) {
        // Return cached response
        res.send(JSON.parse(reply));
      } else {
        // Proceed with the request and store the result in cache
        next();

        const originalSend = res.send;
        res.send = (body: any) => {
          this.client.setex(key, 3600, JSON.stringify(body)); // Cache for 1 hour
          originalSend.call(res, body);
        };
      }
    });
  }
}

3. Service du blog

blog.service.ts :

import { Injectable } from '@nestjs/common';
import * as redis from 'redis';

@Injectable()
export class BlogService {
  private client: redis.RedisClient;

  constructor() {
    this.client = redis.createClient();
  }

  async getPosts(): Promise<any[]> {
    const cachedPosts = await new Promise((resolve, reject) => {
      this.client.get('posts', (err, reply) => {
        if (err) return reject(err);
        resolve(reply ? JSON.parse(reply) : null);
      });
    });

    if (cachedPosts) {
      console.log('Retrieved from cache');
      return cachedPosts;
    }

    const posts = await this.fetchPostsFromDatabase(); // Simulate database fetch
    this.client.setex('posts', 3600, JSON.stringify(posts)); // Cache for 1 hour

    return posts;
  }

  private async fetchPostsFromDatabase(): Promise<any[]> {
    // Simulate database fetch
    console.log('Fetching from database');
    return [
      { id: 1, title: 'First Post', content: 'This is the first post.' },
      { id: 2, title: 'Second Post', content: 'This is the second post.' },
    ];
  }
}

4. Contrôleur du blog

blog.controller.ts :

import { Controller, Get } from '@nestjs/common';
import { BlogService } from './blog.service';

@Controller('blog')
export class BlogController {
  constructor(private readonly blogService: BlogService) {}

  @Get()
  async getPosts() {
    return this.blogService.getPosts();
  }
}

5. Installation et exécution

Commandes terminal :

npm install redis
npm run start

Erreurs fréquentes et debugging

1. Connexion au Redis échoue

Code incorrect :

const client = redis.createClient();

Code correct :

import * as redis from 'redis';

const client = redis.createClient({
  url: 'redis://localhost', // Ensure the URL is correct
});

2. Stockage en cache échoue

Code incorrect :

this.client.setex('posts', 3600, JSON.stringify(posts));

Code correct :

const result = await new Promise((resolve, reject) => {
  this.client.setex('posts', 3600, JSON.stringify(posts), (err) => {
    if (err) return reject(err);
    resolve();
  });
});

3. Récupération depuis le cache échoue

Code incorrect :

this.client.get('posts');

Code correct :

const result = await new Promise((resolve, reject) => {
  this.client.get('posts', (err, reply) => {
    if (err) return reject(err);
    resolve(reply ? JSON.parse(reply) : null);
  });
});

Pour aller plus loin

1. Gestion des erreurs Redis

Concept avancé :

  • Retries et backoffs : Gérer les erreurs Redis en utilisant des réessais et des retards.

Code fonctionnel :

const retry = require('async-retry');

async function fetchData() {
  await retry(async bail => {
    const result = await new Promise((resolve, reject) => {
      this.client.get('posts', (err, reply) => {
        if (err) return reject(err);
        resolve(reply ? JSON.parse(reply) : null);
      });
    });

    if (!result) {
      bail(new Error('Failed to fetch data from Redis'));
    }

    return result;
  }, { retries: 3 });

  // Continue with the rest of the code
}

2. Utilisation de pipelines Redis

Concept avancé :

  • Pipelines : Optimiser les opérations Redis en groupant plusieurs commandes dans une seule requête.

Code fonctionnel :

const pipeline = this.client.pipeline();
pipeline.setex('posts', 3600, JSON.stringify(posts));
pipeline.get('posts');
const result = await pipeline.exec();

const [setResult, getResult] = result;
console.log('Set result:', setResult);
console.log('Get result:', getResult[1]);

3. Authentification Redis

Concept avancé :

  • Authentification : S'authentifier avec un serveur Redis protégé.

Code fonctionnel :

const client = redis.createClient({
  url: 'redis://username:password@localhost',
});

Défi pratique

Défi :

  • Implémenter le caching pour une application de gestionnaire de tâches. Stockez les listes de tâches en mémoire et utilisez le middleware de caching pour améliorer les performances des requêtes.

Ce tutoriel vous a montré comment utiliser Redis avec NestJS pour améliorer la performance de votre application en stockant les résultats d'opérations coûteuses dans une mémoire temporaire. En suivant ces étapes, vous devriez être capable de mettre en œuvre le caching efficacement dans vos projets NestJS.

Besoin d'aide sur NestJS ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Quel est le but du caching avec NestJS et Redis ?
Le but du caching est d'améliorer les performances de l'application en stockant des données temporaires dans la mémoire vive, permettant une accès rapide et réduit les appels à la base de données ou aux services externes.
Comment installer Redis pour utiliser avec NestJS ?
Pour installer Redis, vous pouvez suivre les instructions officielles sur le site web de Redis. Une fois installé, vous devez configurer votre application NestJS en utilisant un module comme `@liaoliaots/nestjs-redis`, qui offre une intégration facile avec Redis.
Quelles sont les principales configurations à faire pour utiliser Redis avec NestJS ?
Pour utiliser Redis avec NestJS, vous devez configurer la connexion en fournissant les informations de connexion telles que l'adresse du serveur Redis, le port et les clés d'accès. Ensuite, vous pouvez créer des services ou des providers qui intégreront Redis pour stocker et récupérer des données.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.