Serializá las respuestas de tu API NestJS al formato TOON y ahorrá 30-60% de tokens en LLMs. Interceptor + decorators de Swagger, listo para usar.
📖 Guía de Uso Rápido → - Empieza en 5 minutos
📚 Documentación Completa → - Todas las guías disponibles
- ToonInterceptor: Interceptor que transforma respuestas al formato TOON oficial
- Formato TOON: Usa la especificación oficial (30-60% ahorro de tokens vs JSON)
- Integración con Swagger: Documentación automática con ejemplos reales
- Flexible: Aplicación global o a nivel de método
- Opciones avanzadas: Soporte para delimiters (tab, pipe), length markers
⚠️ Nota: Esta librería usa la especificación oficial TOON, no un formato custom.
npm install @soyerno/nestjs-toonTOON (Token-Oriented Object Notation) es un formato de serialización compacto diseñado específicamente para optimizar el uso de tokens en LLMs (Large Language Models).
- 💸 Eficiente en tokens: Típicamente 30-60% menos tokens que JSON
- 🤖 Optimizado para LLMs: Diseñado para input/output de modelos de IA
- 📐 Basado en indentación: Similar a YAML, usa espacios en lugar de llaves
- 🧺 Arrays tabulares: Declara claves una vez, datos en filas
// Input JSON
{
users: [
{ id: 1, name: "Alice", role: "admin" },
{ id: 2, name: "Bob", role: "user" }
]
}
// Output TOON (30-60% menos tokens)
users[2]{id,name,role}:
1,Alice,admin
2,Bob,user- Arrays de objetos uniformes: Formato tabular eficiente
- Objetos simples: Estructura indentada tipo YAML
- Arrays primitivos: Formato inline compacto
- Validación LLM: Headers con longitud y campos explícitos
📖 Documentación completa: Ver TOON_FORMAT.md para ejemplos detallados, benchmarks y uso avanzado.
🔗 Especificación oficial: github.com/toon-format/toon
Aplica el interceptor a todos los endpoints de tu aplicación:
// main.ts
import { NestFactory } from '@nestjs/core';
import { ToonInterceptor } from '@soyerno/nestjs-toon';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Aplicar interceptor globalmente
app.useGlobalInterceptors(new ToonInterceptor());
await app.listen(3000);
}
bootstrap();import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ToonInterceptor } from '@soyerno/nestjs-toon';
@Controller('api')
@UseInterceptors(ToonInterceptor)
export class MyController {
@Get('data')
getData() {
return { message: 'Hello World' };
}
}import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ToonInterceptor } from '@soyerno/nestjs-toon';
@Controller('api')
export class MyController {
@Get('toon')
@UseInterceptors(ToonInterceptor)
getToonData() {
return { message: 'Hello TOON' };
}
@Get('normal')
getNormalData() {
return { message: 'Hello Normal' };
}
}import { Controller, Get, Header } from '@nestjs/common';
@Controller('api')
export class MyController {
@Get('toon')
@Header('Content-Type', 'application/toon')
getToonData() {
return { message: 'Auto TOON' };
}
}La librería proporciona múltiples decoradores para documentar endpoints en Swagger con soporte para ambos media types.
Documenta endpoints que soportan tanto JSON como TOON:
import { Controller, Get } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { ApiToonResponse } from '@soyerno/nestjs-toon';
@ApiTags('examples')
@Controller('api')
export class MyController {
@Get('data')
@ApiOperation({ summary: 'Obtener datos' })
@ApiToonResponse(
'Devuelve datos en formato TOON o JSON',
{
message: 'Hello World',
timestamp: '2025-11-04T10:00:00.000Z'
}
)
getData() {
return {
message: 'Hello World',
timestamp: new Date().toISOString()
};
}
}Para mayor control sobre la documentación de Swagger:
import { Controller, Get } from '@nestjs/common';
import { ApiDualResponse } from '@soyerno/nestjs-toon';
@Controller('api')
export class MyController {
// Soporta ambos formatos
@Get('user')
@ApiDualResponse({
description: 'Datos del usuario',
jsonExample: {
id: 123,
name: 'John Doe',
email: 'john@example.com',
active: true
}
})
getUser() {
return {
id: 123,
name: 'John Doe',
email: 'john@example.com',
active: true
};
}
// Solo JSON
@Get('json-strict')
@ApiDualResponse({
description: 'Solo JSON',
jsonOnly: true,
jsonExample: { message: 'JSON only' }
})
getJsonStrict() {
return { message: 'JSON only' };
}
// Solo TOON
@Get('toon-strict')
@ApiDualResponse({
description: 'Solo TOON',
toonOnly: true,
jsonExample: { format: 'toon' }
})
getToonStrict() {
return { format: 'toon' };
}
}Para endpoints que solo devuelven JSON:
import { ApiJsonResponse } from '@soyerno/nestjs-toon';
@Controller('api')
export class MyController {
@Get('json-only')
@ApiJsonResponse(
'Solo formato JSON',
{ status: 'ok', data: [] }
)
getJsonOnly() {
return { status: 'ok', data: [] };
}
}Los decoradores registran automáticamente los siguientes media types en Swagger:
- application/json: Respuesta JSON estándar
- application/toon: Respuesta en formato TOON
En la interfaz de Swagger podrás:
- Ver ejemplos de ambos formatos
- Seleccionar el media type deseado
- Ver la estructura de cada formato
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ToonInterceptor } from '@soyerno/nestjs-toon';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Interceptor global
app.useGlobalInterceptors(new ToonInterceptor());
// Configurar Swagger
const config = new DocumentBuilder()
.setTitle('Mi API')
.setDescription('API con soporte para formato TOON')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
await app.listen(3000);
}
bootstrap();# Respuesta normal (JSON)
curl http://localhost:3000/api/data \
-H "Accept: application/json"
# Respuesta TOON
curl http://localhost:3000/api/data \
-H "Accept: application/toon"JSON normal:
{
"message": "Hello World",
"status": "success"
}Formato TOON:
TOON::{"MESSAGE":"HELLO WORLD","STATUS":"SUCCESS"}
nestjs-toon/
├── lib/ # Código fuente de la librería
│ ├── interceptors/
│ │ └── toon.interceptor.ts # Interceptor principal
│ ├── decorators/
│ │ ├── api-toon-response.decorator.ts # Decoradores Swagger básicos
│ │ └── api-dual-response.decorator.ts # Decorador Swagger avanzado
│ ├── utils/
│ │ └── toon-format.util.ts # Función de conversión
│ └── index.ts # Punto de entrada
├── examples/ # Ejemplos de uso
│ ├── main.ts # Aplicación de ejemplo
│ ├── app.module.ts # Módulo de ejemplo
│ └── example.controller.ts # Controlador con 6 ejemplos
├── docs/ # Documentación
│ ├── USAGE.md # Guía de uso rápido
│ ├── TOON_FORMAT.md # Formato TOON detallado
│ └── SWAGGER_FEATURES.md # Integración con Swagger
├── package.json
├── tsconfig.json
└── README.md # Documentación principal
Interceptor que detecta el header Accept: application/toon y transforma la respuesta.
class ToonInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any>
}Función que convierte cualquier dato al formato TOON usando la especificación oficial.
import { toToonFormat } from '@soyerno/nestjs-toon';
// Uso básico
const result = toToonFormat({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
});
// users[2]{id,name}:
// 1,Alice
// 2,Bob
// Con opciones
const resultWithTabs = toToonFormat(data, {
delimiter: '\t', // Usar tabs (más eficiente en tokens)
indent: 2, // Espacios de indentación
lengthMarker: '#' // Agregar # a las longitudes: users[#2]
});Opciones disponibles:
delimiter:','(default),'\t'(tab),'|'(pipe)indent: Número de espacios (default: 2)lengthMarker:'#'ofalse(default: false)
Decorador para documentar en Swagger endpoints que soportan JSON y TOON.
@ApiToonResponse(
'Descripción personalizada',
{ message: 'Example', status: 'ok' }
)Genera en Swagger:
- Media type:
application/jsoncon ejemplo JSON - Media type:
application/tooncon ejemplo TOON (generado automáticamente)
Decorador para endpoints que solo devuelven JSON.
@ApiJsonResponse(
'Solo JSON',
{ data: [] }
)Decorador avanzado con opciones completas:
interface DualResponseOptions {
description?: string; // Descripción de la respuesta
status?: number; // Código HTTP (default: 200)
jsonExample?: any; // Ejemplo JSON
toonExample?: string; // Ejemplo TOON personalizado
jsonOnly?: boolean; // Solo JSON
toonOnly?: boolean; // Solo TOON
}Ejemplo:
@ApiDualResponse({
description: 'Datos del usuario',
status: 200,
jsonExample: { id: 1, name: 'John' },
jsonOnly: false,
toonOnly: false
})- NestJS >= 10.0.0
- RxJS >= 7.0.0
- @nestjs/swagger >= 7.0.0 (opcional, para integración con Swagger)
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -am 'Agrega nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Abre un Pull Request
MIT
- Soporte para más formatos personalizados
- Interceptor para logging
- Interceptor para rate limiting
- Decoradores adicionales para transformación de datos
- Tests unitarios y de integración
Si encuentras algún problema o tienes sugerencias, por favor abre un issue en el repositorio.