Préparation d'entretien 100% Gratuit angularforall.com

NestJS Senior — Microservices & WS

Nestjs Senior Quiz-Nestjs-Senior Nestjs-Microservices Websockets Performance-Nestjs Nestjs-Securite Architecture-Hexagonale Questions-Entretien-Nestjs-Senior Qcm-Nestjs-Avance Patterns-Nestjs Graphql Node-Js Back-End
Sénior 🔀 Mixte 20 questions ⏱ 15 min
📝

NestJS Senior — Microservices & WS

20 questions avancées NestJS : microservices, WebSockets, performance, sécurité, architecture hexagonale et patterns professionnels. Idéal pour réussir un entretien NestJS Senior.

20 questions ⏱ ~15 min Niveau Sénior

Partager

Banque de révision : les 30 questions corrigées

Voici l'intégralité des 30 questions de « NestJS Senior — Microservices & WS », chacune avec sa bonne réponse et son explication. Le quiz interactif ci-dessus en tire une sélection aléatoire à chaque tentative — utilisez cette liste complète pour réviser.

  • useFactory peut être async et ses dépendances (deps) sont résolues avant l'exécution ; permet la configuration asynchrone (DB, API externe) (bonne réponse)
  • useFactory ne supporte pas l'asynchrone
  • Les dépendances asynchrones doivent être résolues manuellement
  • useFactory est déprécié
Explication : Un provider avec useFactory peut être async. NestJS attendra la résolution de la Promesse avant de rendre le provider disponible. Les dépendances listées dans deps sont injectées avant l'exécution de la factory.
Exemple : { provide: "DB", useFactory: async (config) => { await connect(config); return db; }, inject: [ConfigService] }

  • Implémenter OnApplicationShutdown, app.enableShutdownHooks(), et gérer SIGTERM/SIGINT pour fermer les connexions avant exit (bonne réponse)
  • Utiliser process.exit() directement
  • Les connexions se ferment automatiquement
  • Impossible dans NestJS
Explication : Le graceful shutdown permet de fermer proprement les connexions, terminer les requêtes en cours, puis arrêter l'application. On active app.enableShutdownHooks(), on implémente OnApplicationShutdown dans les services, et on écoute SIGTERM (Kubernetes) et SIGINT (Ctrl+C).
En Kubernetes, le preStop hook peut aussi être utilisé. Timeout recommandé : 30 secondes pour terminer les requêtes.

  • Une classe implémentant CustomTransportStrategy, avec listen() et close() ; permet d'utiliser des protocoles non supportés nativement (bonne réponse)
  • Un transport HTTP personnalisé
  • Un alias pour TCP
  • Un module de logging
Explication : NestJS supporte nativement TCP, Redis, NATS, MQTT, gRPC, Kafka, RabbitMQ. Un custom transport permet d'ajouter son propre protocole (WebSocket personnalisé, AMQP, etc.). On implémente CustomTransportStrategy et on l'utilise avec @MessagePattern().
Exemple : transporter via WebRTC, WebSocket brut, ou un bus personnalisé.

  • ModuleRef permet d'obtenir des providers dynamiquement (get(), resolve()) ; utile pour les providers non injectés statiquement ou le lazy loading (bonne réponse)
  • ModuleRef est un alias du module racine
  • ModuleRef ne permet que d'obtenir des providers statiques
  • ModuleRef est déprécié
Explication : Le ModuleRef donne accès au conteneur d'injection. get(Provider) récupère une instance (singleton). resolve(Provider) crée une nouvelle instance (scope transient). Utile pour les providers non injectés dans le constructeur ou les usines dynamiques.
Attention : resolve() peut créer des fuites mémoire si utilisé sans contextId. Utilisez registerRequestByContextId() pour le scope REQUEST.

  • Étendre LoggerService, implémenter log/error/warn/debug, et utiliser app.useLogger(new CustomLogger()) ; ou utiliser Winston/Pino avec un transport personnalisé (bonne réponse)
  • Utiliser console.log uniquement
  • NestJS n'a pas de système de logging
  • Installer @nestjs/logger
Explication : NestJS permet de remplacer le logger par défaut. On peut créer un service implémentant LoggerService ou utiliser Winston/Pino. Exemple : app.useLogger(new WinstonLogger()). Les transports peuvent être console, fichier, Elasticsearch, Datadog, etc.
En production, utilisez des logs structurés (JSON) pour faciliter l'intégration avec des outils comme ELK ou Loki.

  • ExecutionContext fournit switchToHttp(), getHandler(), getClass() ; permet d'accéder à la requête, aux métadonnées Reflector, et au contexte d'exécution (bonne réponse)
  • Le contexte est une variable globale
  • Les guards n'ont pas accès au contexte
  • ExecutionContext est identique à Request
Explication : Le ExecutionContext (hérité de ArgumentsHost) donne accès au contexte : switchToHttp() pour HTTP, switchToRpc() pour microservices, switchToWs() pour WebSocket. On peut aussi obtenir le handler et la classe pour lire les métadonnées avec Reflector.
Utile pour créer des guards génériques qui s'adaptent au type de transport (HTTP, RPC, WebSocket).

  • Une application qui combine plusieurs stratégies de transport : http.listen() + connectMicroservice() + startAllMicroservices() (bonne réponse)
  • Une application frontend/backend
  • Un mode de développement
  • Un déploiement sur plusieurs serveurs
Explication : Une application hybride combine un serveur HTTP (Express/Fastify) avec des microservices (TCP, Redis, etc.) et des WebSocket. On utilise app.connectMicroservice() pour chaque microservice, puis app.startAllMicroservices() et app.listen().
Exemple : serveur HTTP pour l'API REST + microservice TCP pour la communication interne + Gateway WebSocket pour le temps réel.

  • DEFAULT : singleton ; REQUEST : une instance par requête ; TRANSIENT : nouvelle instance à chaque injection. Les REQUEST scoped peuvent capturer des données spécifiques à la requête (bonne réponse)
  • Tous les providers sont singletons
  • REQUEST scoped est identique à DEFAULT
  • TRANSIENT ne peut pas être utilisé avec les contrôleurs
Explication : Le scope contrôle la durée de vie : DEFAULT (singleton global), REQUEST (instance par requête HTTP, utile pour stocker l'utilisateur courant), TRANSIENT (nouvelle instance à chaque injection). Le scope REQUEST a un coût en performance et nécessite ContextIdFactory pour le partage entre services.
Pour le scope REQUEST, utilisez @Injectable({ scope: Scope.REQUEST }) et @Inject(REQUEST) pour accéder à l'objet requête.

  • Utiliser @nestjs/throttler avec Redis store, ou implémenter un guard personnalisé avec ioredis et sliding window algorithm (bonne réponse)
  • Utiliser express-rate-limit seul
  • NestJS n'a pas de rate limiting
  • Limiter dans le reverse proxy seulement
Explication : @nestjs/throttler supporte Redis store pour le rate limiting distribué (plusieurs instances). On peut aussi implémenter un guard personnalisé avec Redis et un algorithme (token bucket, sliding window, fixed window) pour plus de contrôle.
Algorithme sliding window (plus précis) : stocker les timestamps dans un Redis Sorted Set avec expiration. Token bucket : incrémenter/décrémenter un compteur Redis.

  • Un décorateur qui utilise Reflector et ExecutionContext pour lire les métadonnées, combiné avec un guard pour l'autorisation (bonne réponse)
  • Un décorateur pour les DTOs
  • Un alias pour @Injectable()
  • Un décorateur de classe uniquement
Explication : Pattern : @Roles("admin") définit des métadonnées via @SetMetadata(). Un guard lit ces métadonnées avec Reflector.get() et ExecutionContext, puis compare avec le rôle de l'utilisateur extrait du token JWT.
Combinaison puissante : AuthGuard (authentification) + RolesGuard (autorisation). Permet une sécurité déclarative fine.

  • Utiliser @nestjs/microservices avec des intercepteurs qui propagent les headers (traceId, spanId) via les transporteurs, et exporter vers Jaeger (bonne réponse)
  • C'est automatique
  • NestJS ne supporte pas le tracing
  • Utiliser des logs uniquement
Explication : Le tracing distribué nécessite de propager un contexte (traceId, parentSpanId) entre services. Dans NestJS, on crée un interceptor qui génère/gère les spans, et un transporteur qui propage les headers (ex: avec Redis, Kafka, gRPC metadata). On exporte vers Jaeger/Zipkin via OpenTelemetry.
Utilisez @opentelemetry/instrumentation-nestjs-core pour l'instrumentation automatique. Propager les headers via RpcException ou metadata.

  • Interceptor qui vérifie un header Idempotency-Key dans Redis ; si déjà traité, retourne la réponse en cache ; sinon exécute et stocke le résultat (bonne réponse)
  • Utiliser la méthode GET
  • C'est impossible avec POST
  • Utiliser un verrou de base de données
Explication : L'idempotence pour POST permet d'éviter les doublons (ex: paiement). L'interceptor lit le header Idempotency-Key, vérifie dans Redis si la clé existe. Si oui, retourne la réponse précédente (cachée). Si non, exécute la requête, stocke le résultat avec TTL, et retourne.
Stockez la réponse complète (200, 201, 400, 422). TTL typique : 24h. Gérez le nettoyage automatique avec Redis TTL.

  • Grâce à @nestjs/microservices, on peut ajouter des intercepteurs aux handlers gRPC pour logger, tracer, mesurer les temps, et gérer les métriques (bonne réponse)
  • gRPC ne supporte pas les intercepteurs
  • Les intercepteurs sont automatiques
  • Impossible de monitorer gRPC
Explication : NestJS permet d'utiliser des intercepteurs sur les méthodes @GrpcMethod(). On peut mesurer le temps, logger les requêtes/réponses, capturer les erreurs, et exporter des métriques Prometheus. Le contexte est différent du HTTP (RpcContext).
Pour gRPC, l'interceptor reçoit un RpcContext qui donne accès aux metadata gRPC (trailers, headers).

  • Utiliser @nestjs/cqrs : Commands (write), Queries (read), Events (async), Sagas (orchestration) (bonne réponse)
  • Faire tout dans le contrôleur
  • Utiliser TypeORM uniquement
  • CQRS ne s'applique pas à NestJS
Explication : Le module @nestjs/cqrs fournit : @CommandHandler (mutation), @QueryHandler (lecture), @EventsHandler (réactions asynchrones), Saga (orchestration). Séparation du modèle de lecture (lecture simple) et d'écriture (validation, logique métier).
CQRS est adapté aux domaines complexes (commandes, e-commerce, finance). Pour les CRUD simples, l'architecture standard suffit.

  • Les intercepteurs REQUEST scoped doivent être enregistrés avec useClass, et ils peuvent injecter des providers REQUEST scoped, mais attention aux performances (bonne réponse)
  • Les intercepteurs ne supportent pas le scope REQUEST
  • Injecter un provider REQUEST dans un intercepteur est impossible
  • Les intercepteurs sont toujours singletons
Explication : Les intercepteurs peuvent être REQUEST scoped (@Injectable({ scope: Scope.REQUEST })). Ils peuvent alors injecter des providers REQUEST scoped. Cependant, cela crée une nouvelle instance d'intercepteur par requête, ce qui peut dégrader les performances.
Préférez des intercepteurs singleton (DEFAULT) et passez les données via le contexte (ExecutionContext) plutôt que via des providers REQUEST scoped.

  • Stocker les événements (commande créée, paiement validé) plutôt que l'état courant ; rejouer les événements pour reconstruire l'état. NestJS peut s'intégrer avec EventStoreDB via @nestjs/cqrs et un custom event bus (bonne réponse)
  • Stocker l'état final uniquement
  • Utiliser une base relationnelle standard
  • NestJS ne supporte pas l'event sourcing
Explication : L'event sourcing stocke tous les événements passés, permettant d'auditer, de rejouer, ou de corriger l'état. NestJS avec @nestjs/cqrs gère les événements. L'intégration avec EventStoreDB (ou Kafka) nécessite un custom EventPublisher et EventBus.
Combine CQRS + Event Sourcing : les commands produisent des événements, les queries lisent les projections (vues matérialisées).

  • Utiliser le standalone mode (@nestjs/core/standalone) au lieu de l'HTTP server, réduire le bundle (esbuild), lazy loading des modules, provisionner des "warmers" (bonne réponse)
  • Augmenter la mémoire
  • C'est impossible
  • Utiliser Express au lieu de NestJS
Explication : Pour Lambda : 1) Mode standalone (sans serveur HTTP) : NestFactory.createApplicationContext(). 2) Réduire le bundle avec esbuild (plus rapide que webpack). 3) Lazy loading des modules lourds. 4) Utiliser la provisioned concurrency d'AWS. 5) Optimiser les dépendances.
Le standalone mode est plus léger car il n'initialise pas le serveur HTTP (Express/Fastify). Utilisez @nestjs/axios pour les requêtes HTTP sortantes.

  • Utiliser @nestjs/cache-manager avec ioredis (cluster) ; configurer le store Redis cluster ; les caches sont partagés entre instances (bonne réponse)
  • Utiliser Map() en mémoire
  • NestJS n'a pas de cache distribué
  • Utiliser une base SQL
Explication : Le module @nestjs/cache-manager supporte différents stores. Pour Redis Cluster, on configure cacheManager.registerStore avec ioredis (cluster). Les caches sont alors partagés entre toutes les instances de l'application.
Exemple de décorateur : @CacheKey("users") @CacheTTL(60) async getUsers(). Utilisez des TTL différents selon la volatilité des données.

  • Un filtre qui intercepte HttpException et reformate la réponse selon un standard (ex: { status, title, detail, instance }) (bonne réponse)
  • Un filtre qui log seulement
  • Un filtre qui ignore les erreurs
  • NestJS a déjà un format standard
Explication : Un exception filter personnalisé capture toutes les exceptions (catch(exception, host)) et transforme la réponse. On peut implémenter JSON:API (errors object), RFC 7807 (Problem Details), ou un format maison. Utile pour la cohérence entre microservices.
Exemple de format standardisé : { "type": "/errors/validation", "title": "Validation Failed", "status": 422, "details": [ ... ] }

  • Utiliser @nestjs/axios avec interceptors + rxjs retryWhen/delayWhen, ou implémenter un custom decorator avec exponential backoff (jitter) (bonne réponse)
  • Faire un simple try/catch
  • Les retries sont automatiques
  • NestJS n'a pas de mécanisme de retry
Explication : Pattern de retry avec backoff : échec → attendre 1s → réessayer → échec → attendre 2s → etc. Avec RxJS : retryWhen(errors => errors.pipe(delayWhen(() => timer(backoff)))). On peut ajouter du jitter (variation aléatoire) pour éviter l'effet de meute.
Backoff exponentiel avec jitter : 100ms, 200ms, 400ms, 800ms... Maximum 10 retries. Idempotence importante pour les retries.

  • Un module configurable avec des options qui peuvent être asynchrones (ex: lire depuis ConfigService). forRootAsync() permet d'injecter des providers avant la configuration (bonne réponse)
  • Un module créé à l'exécution
  • Un module sans configuration
  • Un module uniquement pour les tests
Explication : Un module dynamique (forRootAsync) permet de configurer le module avec des options asynchrones (DB, API). Exemple : TypeOrmModule.forRootAsync({ useFactory: (config) => config.get("db"), inject: [ConfigService] }). Utile quand la config vient d'un service.
Pattern : registerAsync et forRootAsync. Utilisez useFactory, useClass, ou useExisting pour fournir les options.

  • Utiliser les metadata des transporteurs (ex: Kafka headers, gRPC metadata) pour propager le contexte, et un interceptor pour l'injecter dans le service (bonne réponse)
  • C'est automatique
  • La propagation n'est pas possible
  • Utiliser des variables globales
Explication : Pour propager le contexte (traceId, userId) entre microservices : 1) Client side : ajouter des metadata/headers. 2) Server side : extraire les metadata dans un interceptor et les stocker dans le contexte (AsyncLocalStorage). Tous les services peuvent alors accéder au contexte.
AsyncLocalStorage (Node.js natif) ou cls-hooked pour maintenir le contexte à travers les appels asynchrones.

  • Utiliser @nestjs/axios avec un interceptor et un état (CLOSED/OPEN/HALF-OPEN) ; ouvrir le circuit après N échecs, le fermer après un timeout (bonne réponse)
  • Faire un simple try/catch
  • NestJS a un circuit breaker intégré
  • Utiliser une base de données
Explication : Le circuit breaker évite les appels inutiles à un service défaillant. États : CLOSED (appels normaux), OPEN (rejet immédiat, fail fast), HALF-OPEN (test après timeout). On peut utiliser une lib comme opossum ou implémenter avec un interceptor et Redis (distribué).
Combinez circuit breaker + retry + timeout + fallback (cache, réponse par défaut). Pattern "Resilience4j" en Java.

  • createParamDecorator((data, ctx) => ctx.switchToHttp().getRequest().user) ; retourne l'objet utilisateur typé (ex: CurrentUser) (bonne réponse)
  • Utiliser @Req().user
  • Impossible de typer automatiquement
  • Utiliser @Query()
Explication : Un custom param decorator extrait l'utilisateur du token JWT (ajouté par AuthGuard) et le retourne typé. Exemple : @CurrentUser() user: UserDto. On peut aussi passer des paramètres (ex: @CurrentUser("id") userId: string).
Le decorator peut être générique : @CurrentUser() user: T. On peut aussi y ajouter des validations.

  • NestJS supporte @ResolveReference() pour la fédération ; chaque service expose son sous-graphe, Apollo Gateway orchestre (bonne réponse)
  • NestJS ne supporte pas GraphQL
  • La fédération est automatique
  • Utiliser REST à la place
Explication : GraphQL Federation permet de composer plusieurs services GraphQL en un seul graphe. Dans NestJS, on utilise @ResolveReference() pour implémenter l'entité fédérée. Le service expose son schéma, Apollo Gateway les agrège.
Décorateurs : @EntityReference(), @ResolveReference(). Le gateway doit être configuré avec les URLs des sous-graphes.

  • Utiliser @ValidatorConstraint({ async: true }) et injecter le service via le contexte (ValidationArguments) ou un module global (bonne réponse)
  • Impossible, class-validator ne supporte pas l'asynchrone
  • Faire la validation dans le contrôleur
  • Utiliser un guard
Explication : On peut créer des validateurs asynchrones avec @ValidatorConstraint({ async: true }). Pour injecter un service (ex: UserService), on utilise ValidationArguments ou un module global avec setContainer (class-validator).
Exemple : @IsEmailUnique() qui vérifie en base que l'email n'existe pas. Nécessite class-validator@0.14+.

  • NestFactory.createApplicationContext() crée une app sans serveur HTTP ; permet d'utiliser l'injection de dépendances pour les scripts, cron jobs, workers (bonne réponse)
  • Impossible, NestJS nécessite un serveur HTTP
  • Utiliser main.ts sans listen()
  • Créer une application Express séparée
Explication : Le standalone mode (NestFactory.createApplicationContext()) crée une application NestJS sans serveur HTTP. Tous les modules, services, et l'injection de dépendances fonctionnent. Idéal pour : scripts CLI, workers background, cron jobs, consommateurs Kafka.
Exemple : script de seeding de base de données, import CSV, envoi d'emails batch. Fermez proprement avec app.close().

  • Utiliser ClassSerializerInterceptor avec @Exclude() dans les DTOs, et configurer le groupe (@Groups) pour des contextes différents (bonne réponse)
  • Supprimer manuellement dans le service
  • NestJS n'a pas de sérialisation
  • Utiliser JSON.stringify()
Explication : Le ClassSerializerInterceptor utilise class-transformer pour sérialiser les objets. On peut exclure des propriétés (@Exclude()), exposer (@Expose()), ou grouper (@Groups(["admin"])) pour des contextes différents.
Activation : @UseInterceptors(ClassSerializerInterceptor) sur le contrôleur. Transforme aussi les dates (@Type(() => Date)).

  • Utiliser un interceptor qui capture l'utilisateur courant (via Context) et l'ajoute aux entités, ou un subscriber TypeORM (beforeUpdate/afterUpdate) (bonne réponse)
  • Logger toutes les requêtes SQL
  • C'est impossible
  • Utiliser un trigger PostgreSQL
Explication : Approches : 1) Interceptor HTTP + service d'audit (enregistre qui a fait quoi, avant/après). 2) Subscriber TypeORM (@EventSubscriber()) qui capture les changements au niveau ORM. 3) Triggers PostgreSQL avec table d'audit (plus bas niveau).
Audit log minimal : userId, action, table, ancienne_valeur, nouvelle_valeur, timestamp. Pour la GDPR, permettre l'export des logs par utilisateur.

  • NestJS supporte Kafka via @nestjs/microservices ; on peut configurer groupId, partitions, consumer groups, et gérer la reprise après échec avec commit manuel (bonne réponse)
  • Kafka n'est pas supporté
  • Les groupes sont automatiques
  • Utiliser RabbitMQ à la place
Explication : NestJS a un transport Kafka intégré. On peut configurer consumer: { groupId: "my-group" }. La reprise après échec : commit: false pour commit manuel après traitement réussi. Les partitions sont gérées automatiquement (rééquilibrage).
Pattern dead letter queue : consommer les messages invalides vers un topic DLQ. Gérer le rejeu des messages avec des offsets personnalisés.