npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-toon

v0.1.0

Published

NestJS library for TOON (Token-Oriented Object Notation) serialization support

Readme

nestjs-toon

npm version License: MIT

NestJS library for TOON (Token-Oriented Object Notation) serialization support. Reduce API response tokens by 30-60% for LLM-powered applications.

Built on @toon-format/toon - This library provides NestJS integration for the official TOON format library. Full credit to the TOON format creators for the core serialization implementation.

What is TOON?

TOON (Token-Oriented Object Notation) is a compact, human-readable format designed specifically for Large Language Model contexts. It merges YAML-style indentation with CSV-like tabular arrays to minimize token usage while maintaining structure clarity.

Key Benefits:

  • 📉 30-60% fewer tokens compared to JSON
  • 📊 Best for uniform arrays of objects (common API responses)
  • 🔄 Lossless round-trips - deterministic encoding/decoding
  • 🎯 Drop-in replacement for JSON with content negotiation

Installation

npm install nestjs-toon
# or
yarn add nestjs-toon
# or
pnpm add nestjs-toon

Quick Start

1. Global Configuration

Register the module globally in your app:

// app.module.ts
import { Module } from '@nestjs/common';
import { ToonModule } from 'nestjs-toon';

@Module({
  imports: [
    ToonModule.forRoot({
      global: true,
      enableResponseSerialization: true,
    }),
  ],
})
export class AppModule {}

2. Create a Controller

// users.controller.ts
import { Controller, Get } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return {
      users: [
        { id: 1, name: 'Alice', email: '[email protected]', age: 30 },
        { id: 2, name: 'Bob', email: '[email protected]', age: 25 },
      ],
    };
  }
}

3. Test with cURL

TOON Response:

curl -H "Accept: text/toon" http://localhost:3000/users
users[2]{id,name,email,age}:
1,Alice,[email protected],30
2,Bob,[email protected],25

JSON Response (default):

curl -H "Accept: application/json" http://localhost:3000/users
{
  "users": [
    { "id": 1, "name": "Alice", "email": "[email protected]", "age": 30 },
    { "id": 2, "name": "Bob", "email": "[email protected]", "age": 25 }
  ]
}

Configuration Options

interface ToonModuleOptions {
  // Enable response serialization (Object → TOON)
  enableResponseSerialization?: boolean; // default: true

  // Enable request deserialization (TOON → Object)
  enableRequestDeserialization?: boolean; // default: true

  // Apply interceptors globally
  global?: boolean; // default: false

  // Error handling strategy
  errorHandling?: 'throw' | 'log-and-fallback' | 'silent'; // default: 'throw'

  // Custom content type
  contentType?: string; // default: 'text/toon'

  // Max request body size in bytes
  maxBodySize?: number; // default: 102400 (100KB)

  // Request body parsing timeout in milliseconds
  parseTimeout?: number; // default: 30000 (30 seconds)

  // Sanitize error messages in production
  sanitizeErrors?: boolean; // default: true
}

Integration Methods

Method 1: Global Registration

Apply TOON serialization to all endpoints:

ToonModule.forRoot({
  global: true,
  enableResponseSerialization: true,
  errorHandling: 'log-and-fallback',
})

Method 2: Async Configuration

Use with ConfigModule for dynamic configuration:

import { ConfigModule, ConfigService } from '@nestjs/config';

ToonModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    global: config.get('TOON_GLOBAL') === 'true',
    enableResponseSerialization: true,
    errorHandling: config.get('TOON_ERROR_HANDLING') || 'throw',
  }),
  inject: [ConfigService],
})

Method 3: Selective Endpoints

Use decorators for fine-grained control:

import { Controller, Get } from '@nestjs/common';
import { UseToon } from 'nestjs-toon';

@Controller('data')
export class DataController {
  @Get()
  @UseToon() // Only this endpoint supports TOON
  getData() {
    return { items: [...] };
  }
}

Content Negotiation

The library uses standard HTTP content negotiation via the Accept header:

| Accept Header | Response Format | |--------------|-----------------| | text/toon | TOON | | application/json | JSON | | */* | JSON (TOON must be explicitly requested) | | text/* | TOON | | Not specified | JSON |

Request Body Parsing

The library supports parsing TOON request bodies when enableRequestDeserialization is enabled:

Sending TOON Data

curl -X POST http://localhost:3000/users \
  -H "Content-Type: text/toon" \
  -H "Accept: text/toon" \
  -d 'name: Alice
email: [email protected]
age: 30'

Using @ToonBody() Decorator

import { Controller, Post } from '@nestjs/common';
import { ToonBody } from 'nestjs-toon';

@Controller('users')
export class UsersController {
  @Post()
  create(@ToonBody() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Post('bulk')
  createBulk(@ToonBody('users') users: CreateUserDto[]) {
    // Extract specific property from TOON body
    return this.usersService.createMany(users);
  }
}

Note: The middleware automatically parses TOON bodies and populates req.body. You can use the standard @Body() decorator or the @ToonBody() decorator (they work the same way).

Error Handling

Three error handling modes are available:

1. throw (default)

Throws exceptions on serialization errors:

ToonModule.forRoot({
  errorHandling: 'throw',
})

2. log-and-fallback

Logs errors and falls back to JSON:

ToonModule.forRoot({
  errorHandling: 'log-and-fallback',
})

Response headers on fallback:

  • Content-Type: application/json
  • X-Toon-Fallback: true

3. silent

Silently falls back to JSON without logging:

ToonModule.forRoot({
  errorHandling: 'silent',
})

Security Configuration

nestjs-toon includes built-in security protections against common web vulnerabilities. These protections are enabled by default but can be customized as needed.

Request Body Size Limits

Prevent memory exhaustion by limiting the size of incoming TOON request bodies:

ToonModule.forRoot({
  maxBodySize: 102400, // 100KB (default)
})

Security benefit: Protects against attackers sending extremely large payloads to consume server memory.

Request Parsing Timeout

Prevent slow-loris attacks by setting a timeout for request body parsing:

ToonModule.forRoot({
  parseTimeout: 30000, // 30 seconds (default)
})

Security benefit: Limits how long the server waits for a request body, preventing attackers from holding connections open indefinitely with slow data transmission.

Production Error Sanitization

Automatically hide detailed error messages in production to prevent information disclosure:

ToonModule.forRoot({
  sanitizeErrors: true, // default
})

When NODE_ENV=production and sanitizeErrors: true:

  • ✅ Generic error messages are returned to clients
  • ❌ Internal parser details are hidden
  • ❌ Stack traces are not exposed

When NODE_ENV=development or sanitizeErrors: false:

  • ✅ Detailed error messages help with debugging
  • ✅ Original error details included in responses

Security benefit: Prevents attackers from gathering information about your internal implementation or discovering parser vulnerabilities through detailed error messages.

Content-Type Validation

The module automatically validates custom content types to prevent HTTP header injection:

// ✅ Valid
ToonModule.forRoot({
  contentType: 'text/toon',
})

// ✅ Valid
ToonModule.forRoot({
  contentType: 'application/x-custom+json',
})

// ❌ Throws error - contains newlines (header injection risk)
ToonModule.forRoot({
  contentType: 'text/toon\r\nX-Evil: injected',
})

Security benefit: Prevents HTTP response splitting and header injection attacks.

Accept Header Limits

The content negotiation system enforces limits on Accept header parsing to prevent DoS attacks:

  • Maximum Accept header size: 8KB (RFC 7230 recommendation)
  • Maximum media types parsed: 20

Security benefit: Prevents CPU exhaustion from parsing maliciously large Accept headers.

Security Best Practices

  1. Always use HTTPS in production - TOON data is text-based and should be transmitted over encrypted connections
  2. Keep request body limits appropriate - Set maxBodySize based on your actual needs (default 100KB is suitable for most APIs)
  3. Monitor timeout settings - Adjust parseTimeout if you have legitimate use cases for slow connections, but keep it as low as practical
  4. Never disable error sanitization in production - Keep sanitizeErrors: true when NODE_ENV=production
  5. Use standard NestJS security practices - Apply helmet, CORS, rate limiting, and other standard security middleware

Performance

Token Reduction Examples

100-item user array:

  • JSON: ~3,500 tokens
  • TOON: ~1,400 tokens
  • Reduction: 60%

500 e-commerce orders:

  • JSON: ~11,842 tokens
  • TOON: ~4,617 tokens
  • Reduction: 61%

When to Use TOON

Use TOON for:

  • Uniform arrays of objects (API lists, tables)
  • LLM-powered applications where tokens matter
  • Cost-sensitive AI workflows
  • Large datasets with repeated structure

Stick with JSON for:

  • Deeply nested structures (>5 levels)
  • Purely flat data (use CSV instead)
  • Binary data or file uploads
  • Legacy systems requiring JSON

API Reference

Exports

// Module
export { ToonModule } from 'nestjs-toon';

// Services
export { ToonSerializerService } from 'nestjs-toon';

// Interceptors
export { ToonResponseInterceptor } from 'nestjs-toon';

// Middleware
export { ToonBodyParserMiddleware } from 'nestjs-toon';

// Decorators
export { UseToon, ToonBody } from 'nestjs-toon';

// Interfaces
export { ToonModuleOptions } from 'nestjs-toon';

// Constants
export {
  TOON_CONTENT_TYPE,
  TOON_MODULE_OPTIONS,
  TOON_ENABLED_METADATA,
  TOON_FALLBACK_HEADER,
} from 'nestjs-toon';

// Exceptions
export {
  ToonSerializationException,
  ToonDeserializationException,
} from 'nestjs-toon';

// Utilities
export {
  acceptsToon,
  parseAcceptHeader,
  negotiateContentType,
  handleToonError,
  isToonError,
} from 'nestjs-toon';

ToonSerializerService

Injectable service for manual TOON encoding/decoding:

import { Injectable } from '@nestjs/common';
import { ToonSerializerService } from 'nestjs-toon';

@Injectable()
export class MyService {
  constructor(private toonSerializer: ToonSerializerService) {}

  processData() {
    const data = { users: [...] };

    // Encode to TOON
    const toonString = this.toonSerializer.encode(data);

    // Decode from TOON
    const decoded = this.toonSerializer.decode(toonString);

    // Check if data can be serialized
    const canEncode = this.toonSerializer.canSerialize(data);
  }
}

Middleware

The ToonBodyParserMiddleware is automatically configured when you import ToonModule. It:

  • Parses request bodies with Content-Type: text/toon
  • Enforces configurable size limits (default 100KB)
  • Populates req.body with decoded TOON data
  • Handles errors based on errorHandling configuration

Manual Configuration (optional):

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { ToonBodyParserMiddleware } from 'nestjs-toon';

@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(ToonBodyParserMiddleware)
      .forRoutes('*'); // Apply to all routes
  }
}

Example Application

See the example/ directory for a complete working example with:

  • Global TOON configuration
  • Multiple endpoints demonstrating different data structures
  • cURL examples for testing
  • Token comparison benchmarks
cd example
npm install
npm start

Edge Cases

Circular References

TOON cannot serialize circular references. They will throw ToonSerializationException:

const data: any = { name: 'Alice' };
data.self = data; // Circular reference

// Throws: ToonSerializationException: Circular reference detected

Type Preservation

TOON is lossy for some JavaScript types:

  • ✅ Objects, arrays, strings, numbers, booleans, null
  • ❌ Date objects (convert to ISO strings first)
  • ❌ RegExp, functions, symbols, undefined

Large Payloads

For responses >10MB, consider streaming (coming in future versions).

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT © Attila Papai

Resources

Support


Made with ❤️ for the NestJS and AI communities