Saltearse al contenido

Contexto y Rastreo de Peticiones

Aprende cómo rastrear y gestionar peticiones en tus aplicaciones Veloce-TS con IDs de request automáticos, timeouts y soporte de cancelación.

Veloce-TS proporciona gestión mejorada del contexto de peticiones que te permite:

  • Request IDs Automáticos - Cada petición recibe un UUID único
  • Rastreo de Peticiones - Rastrea peticiones a través de logs y headers
  • Soporte de Cancelación - Cancela operaciones largas con AbortSignal
  • Gestión de Timeouts - Configura timeouts por ruta o globalmente
  • Almacenamiento de Metadatos - Adjunta datos personalizados a peticiones
  • Integración con Logging - El Request ID se propaga por todos los logs
import { VeloceTS, createRequestContextMiddleware } from 'veloce-ts';
const app = new VeloceTS();
// Habilitar request context con logging
app.use(createRequestContextMiddleware({
timeout: 30000, // Timeout por defecto de 30 segundos
logging: true, // Habilitar logging de peticiones
logHeaders: ['user-agent', 'x-forwarded-for']
}));
import { Controller, Get, RequestId } from 'veloce-ts';
@Controller('/users')
class UserController {
@Get('/:id')
async getUser(
@Param('id') id: string,
@RequestId() requestId: string
) {
logger.info({ requestId }, 'Obteniendo usuario');
return await db.users.findById(id);
}
}

Cada petición recibe automáticamente un UUID único:

// Llega una petición
// -> UUID generado: "abc-123-def-456"
// -> Disponible en handlers vía @RequestId()
// -> Enviado de vuelta en header X-Request-ID

En Controllers:

@Controller('/api')
class ApiController {
@Get('/data')
async getData(@RequestId() requestId: string) {
console.log('Request ID:', requestId);
return { data: 'ejemplo', requestId };
}
}

En API Funcional:

import { getRequestId } from 'veloce-ts';
app.get('/data', {
handler: async (c) => {
const requestId = getRequestId(c);
logger.info({ requestId }, 'Procesando petición');
return { data: 'ejemplo', requestId };
}
});

Desde Context:

import { getRequestId } from 'veloce-ts';
app.use(async (c, next) => {
const requestId = getRequestId(c);
console.log('Middleware - Request ID:', requestId);
await next();
});

El Request ID se incluye automáticamente en los headers de respuesta:

HTTP/1.1 200 OK
X-Request-ID: abc-123-def-456
Content-Type: application/json

Proporciona tu propio request ID vía header:

Ventana de terminal
curl -H "X-Request-ID: custom-id-123" http://localhost:3000/api/data

El middleware usará el ID proporcionado si está disponible.

Usa AbortSignal para cancelar operaciones largas:

@Controller('/tasks')
class TaskController {
@Get('/process')
async processTask(@AbortSignal() signal: AbortSignal) {
// Pasar signal a operaciones largas
return await heavyComputation({ signal });
}
@Get('/download')
async downloadFile(@AbortSignal() signal: AbortSignal) {
// Cancelar descarga si el cliente se desconecta
return await streamLargeFile({ signal });
}
}
@Get('/external')
async fetchExternal(@AbortSignal() signal: AbortSignal) {
try {
const response = await fetch('https://api.example.com/data', {
signal // Pasar signal a fetch
});
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
return { error: 'La petición fue cancelada' };
}
throw error;
}
}
@Get('/long-task')
async longTask(@AbortSignal() signal: AbortSignal) {
for (let i = 0; i < 100; i++) {
// Verificar si fue cancelado
if (signal.aborted) {
return { cancelled: true, progress: i };
}
await processChunk(i);
}
return { completed: true };
}
@Get('/streaming')
async streamData(@AbortSignal() signal: AbortSignal) {
signal.addEventListener('abort', () => {
console.log('Petición cancelada, limpiando...');
// Limpiar recursos
});
return await streamLargeDataset();
}

Establecer timeout por defecto para todas las peticiones:

app.use(createRequestContextMiddleware({
timeout: 30000 // 30 segundos para todas las peticiones
}));

Sobreescribir timeout para rutas específicas:

// API con Decoradores
@Get('/slow-operation')
@Timeout(60000) // 60 segundos
async slowOperation() {
return await verySlowProcess();
}
// API Funcional
app.get('/slow', {
timeout: 60000,
handler: async () => {
return await verySlowProcess();
}
});

Cuando ocurre un timeout, la petición se aborta automáticamente:

@Get('/task')
async task(@AbortSignal() signal: AbortSignal) {
try {
const result = await longRunningTask({ signal });
return { result };
} catch (error) {
if (signal.aborted) {
// Ocurrió timeout
return { error: 'Timeout de petición' };
}
throw error;
}
}

Cuando usas el middleware de request context con logging habilitado, el request ID aparece automáticamente en todos los logs:

import { getLogger } from 'veloce-ts';
@Controller('/api')
class ApiController {
private logger = getLogger().child({ component: 'ApiController' });
@Get('/data')
async getData(@RequestId() requestId: string) {
// Todos estos logs incluirán el request ID
this.logger.info('Iniciando obtención de datos');
this.logger.debug('Consultando base de datos');
this.logger.info('Obtención de datos completada');
return { data: 'ejemplo' };
}
}

Salida de log:

[2024-01-15 10:23:45] [requestId: abc-123] INFO: Iniciando obtención de datos
[2024-01-15 10:23:45] [requestId: abc-123] DEBUG: Consultando base de datos
[2024-01-15 10:23:45] [requestId: abc-123] INFO: Obtención de datos completada
@Get('/users/:id')
async getUser(
@Param('id') id: string,
@RequestId() requestId: string
) {
const logger = getLogger().child({
requestId,
userId: id,
component: 'UserController'
});
logger.info('Obteniendo usuario');
try {
const user = await db.users.findById(id);
logger.info('Usuario obtenido exitosamente');
return user;
} catch (error) {
logger.error('Fallo al obtener usuario', error);
throw error;
}
}

Almacena datos personalizados en el contexto de la petición:

import { setRequestMetadata, getRequestMetadata } from 'veloce-ts';
// En middleware
app.use(async (c, next) => {
setRequestMetadata(c, 'startTime', Date.now());
await next();
const duration = Date.now() - getRequestMetadata(c, 'startTime');
console.log(`La petición tomó ${duration}ms`);
});
// En handler
@Get('/data')
async getData(@Ctx() ctx: Context) {
setRequestMetadata(ctx, 'cacheHit', true);
return { data: 'ejemplo' };
}

Habilítalo temprano en tu cadena de middleware:

const app = new VeloceTS();
// Primer middleware
app.use(createRequestContextMiddleware({ logging: true }));
// Luego otro middleware
app.useCors();
app.useRateLimit({ max: 100 });

2. Incluye Request ID en Respuestas de Error

Sección titulada «2. Incluye Request ID en Respuestas de Error»
app.onError((error, c) => {
const requestId = getRequestId(c);
return c.json({
error: error.message,
requestId,
timestamp: new Date().toISOString()
}, error.status || 500);
});

3. Pasa AbortSignal a Todas las Operaciones Async

Sección titulada «3. Pasa AbortSignal a Todas las Operaciones Async»
@Get('/data')
async getData(@AbortSignal() signal: AbortSignal) {
// Pasar a queries de base de datos
const users = await db.users.findMany({ signal });
// Pasar a APIs externas
const external = await fetch('https://api.example.com', { signal });
// Pasar a funciones personalizadas
const processed = await processData(users, { signal });
return processed;
}
import {
VeloceTS,
Controller,
Get,
Post,
Param,
Body,
RequestId,
AbortSignal,
Ctx,
createRequestContextMiddleware,
getLogger
} from 'veloce-ts';
const app = new VeloceTS();
// Habilitar request context
app.use(createRequestContextMiddleware({
timeout: 30000,
logging: true,
logHeaders: ['user-agent']
}));
@Controller('/api')
class ApiController {
private logger = getLogger().child({ component: 'ApiController' });
@Get('/users/:id')
async getUser(
@Param('id') id: string,
@RequestId() requestId: string,
@AbortSignal() signal: AbortSignal
) {
this.logger.info({ requestId, userId: id }, 'Obteniendo usuario');
try {
if (signal.aborted) {
return { error: 'Petición cancelada' };
}
const user = await db.users.findById(id, { signal });
this.logger.info({ requestId, userId: id }, 'Usuario obtenido');
return { user, requestId };
} catch (error) {
this.logger.error({ requestId, userId: id, error }, 'Fallo al obtener usuario');
throw error;
}
}
}
app.include(ApiController);
app.listen(3000);
  • Aprende sobre Caché para reducir tiempo de procesamiento
  • Explora Logging para estrategias avanzadas de logging
  • Revisa Manejo de Errores para gestión apropiada de errores