Quand utiliser
Utilisez cette compétence lorsque vous implémentez la validation des requêtes, les contrôles d'autorisation, le mapping des réponses, la journalisation, la gestion des erreurs ou le suivi des performances sur les méthodes d'outils NitroStack.
1. Guards (Guard et @UseGuards)
Les Guards déterminent si une requête doit être traitée par un gestionnaire d'outil en fonction de l'authentification, de l'autorisation ou d'autres conditions.
Interface :
import { Guard, ExecutionContext } from '@nitrostack/core';
export interface Guard {
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
}
Exemple :
import { Guard, ExecutionContext, Injectable } from '@nitrostack/core';
@Injectable()
export class RolesGuard implements Guard {
async canActivate(context: ExecutionContext): Promise<boolean> {
const userRoles = context.clientMetadata?.roles || [];
return userRoles.includes('admin');
}
}
Appliquez le guard avec @UseGuards(...) :
import { Tool, UseGuards, z } from '@nitrostack/core';
import { RolesGuard } from './roles.guard.js';
export class AdminTools {
@Tool({
name: 'delete_system_logs',
description: 'Supprime tous les journaux système du serveur.',
inputSchema: z.object({}),
})
@UseGuards(RolesGuard)
async deleteLogs() {
return { success: true };
}
}
2. Intercepteurs (InterceptorInterface et @UseInterceptors)
Les intercepteurs peuvent transformer/intercepter les arguments d'entrée ou la sortie mappée d'une exécution de méthode d'outil.
Interface :
import { ExecutionContext } from '@nitrostack/core';
export interface InterceptorInterface {
intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown>;
}
Exemple :
import { InterceptorInterface, ExecutionContext, Injectable } from '@nitrostack/core';
@Injectable()
export class TimingInterceptor implements InterceptorInterface {
async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
const start = Date.now();
const result = await next();
const duration = Date.now() - start;
context.logger.info(`Exécution terminée en ${duration}ms`);
return {
...result,
_meta: { durationMs: duration }
};
}
}
3. Filtres d'exception (ExceptionFilterInterface et @UseFilters)
Les filtres d'exception attrapent toutes les erreurs levées dans les guards, intercepteurs ou gestionnaires d'outils eux-mêmes, et les transforment en charges utiles JSON compréhensibles par l'utilisateur.
Interface :
import { ExecutionContext } from '@nitrostack/core';
export interface ExceptionFilterInterface {
catch(exception: unknown, context: ExecutionContext): unknown | Promise<unknown>;
}
Exemple :
import { ExceptionFilterInterface, ExecutionContext, Injectable } from '@nitrostack/core';
@Injectable()
export class CustomExceptionFilter implements ExceptionFilterInterface {
catch(exception: unknown, context: ExecutionContext) {
const message = exception instanceof Error ? exception.message : 'Erreur inconnue';
return {
error: true,
message,
timestamp: new Date().toISOString()
};
}
}
Appliquez le filtre avec @UseFilters(...) sur une méthode d'outil :
import { Tool, UseFilters, z } from '@nitrostack/core';
import { CustomExceptionFilter } from './custom-exception.filter.js';
export class LoggingTools {
@Tool({
name: 'generate_report',
description: 'Génère des rapports d'utilisation du système.',
inputSchema: z.object({}),
})
@UseFilters(CustomExceptionFilter)
async generateReport() {
throw new Error('La génération de rapport n'est pas encore implémentée.');
}
}
4. Middleware (MiddlewareInterface, @Middleware et @UseMiddleware)
Le middleware s'exécute avant que la requête n'atteigne le gestionnaire d'outil, et peut encapsuler l'exécution du gestionnaire en invoquant next().
Interface :
import { ExecutionContext } from '@nitrostack/core';
export interface MiddlewareInterface {
use(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown>;
}
Exemple :
import { Middleware, MiddlewareInterface, ExecutionContext } from '@nitrostack/core';
@Middleware()
export class LoggingMiddleware implements MiddlewareInterface {
async use(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
context.logger.info(`Entrée dans l'outil : ${context.toolName}`);
try {
const result = await next();
context.logger.info(`Sortie de l'outil : ${context.toolName}`);
return result;
} catch (error) {
context.logger.error(`Erreur dans l'outil : ${error}`);
throw error;
}
}
}
Appliquez le middleware avec @UseMiddleware(...) sur une méthode d'outil :
import { Tool, UseMiddleware, z } from '@nitrostack/core';
import { LoggingMiddleware } from './logging.middleware.js';
export class StationTools {
@Tool({
name: 'fetch_logs',
description: 'Récupère les journaux d'opérations de la station.',
inputSchema: z.object({}),
})
@UseMiddleware(LoggingMiddleware)
async fetchLogs() {
return { status: 'operationnel' };
}
}
5. Pipes (PipeInterface, @Pipe et @UsePipes)
Les pipes sont utilisés pour transformer ou valider les arguments d'entrée avant qu'ils n'atteignent la méthode du gestionnaire d'outil.
Interface :
import { ArgumentMetadata } from '@nitrostack/core';
export interface PipeInterface<T = unknown, R = unknown> {
transform(value: T, metadata: ArgumentMetadata): R | Promise<R>;
}
Exemple :
import { Pipe, PipeInterface, ArgumentMetadata } from '@nitrostack/core';
@Pipe()
export class TrimPipe implements PipeInterface<Record<string, unknown>, Record<string, unknown>> {
transform(value: Record<string, unknown>, metadata: ArgumentMetadata) {
const trimmed: Record<string, unknown> = {};
for (const [key, val] of Object.entries(value)) {
trimmed[key] = typeof val === 'string' ? val.trim() : val;
}
return trimmed;
}
}
Appliquez le pipe avec @UsePipes(...) sur une méthode d'outil :
import { Tool, UsePipes, z } from '@nitrostack/core';
import { TrimPipe } from './trim.pipe.js';
export class MessagingTools {
@Tool({
name: 'send_message',
description: 'Envoyer un message aux autres stations.',
inputSchema: z.object({ text: z.string() }),
})
@UsePipes(TrimPipe)
async sendMessage(input: { text: string }) {
return { sentText: input.text };
}
}