Du har læst dokumentationen, du ved, at TOON halverer tokenantal på tabeldata. Nu vil du faktisk koble det ind i noget. Denne artikel handler om rørene: læsning og skrivning af .toon-filer, validering af TOON ved systemgrænser, bygning af en Express-middleware, der parser TOON-anmodningsorganer, og samling af en database-til-prompt-rørledning, der sender TOON direkte til en LLM. Rigtig kode, rigtige mønstre — ingen legetøjseksempler.

Opsætning

Installer pakken fra npm. Den er ESM-only, så du har brug for "type": "module" i din package.json eller brug en .mjs-udvidelse. Node.js 18+ er alt, hvad du har brug for — ingen konfigurationsfiler, ingen plugins.

bash
npm install @toon-format/toon
js
import { encode, decode } from '@toon-format/toon';

// That's it. encode() → TOON string, decode() → JS value.

Læsning og skrivning af TOON-filer

Node.js' fs-modul håndterer I/O. Send filindholdet direkte ind i decode(), eller send dine data til encode() og skriv resultatet til disk. Nedenfor er begge mønstre — synkront til scripts og CLI-værktøjer, asynkront til serverruter.

js
// --- Sync (scripts, CLI tools) ---
import { readFileSync, writeFileSync } from 'fs';
import { encode, decode } from '@toon-format/toon';

// Read a .toon file and decode it to a JS value
const raw = readFileSync('./data/products.toon', 'utf8');
const products = decode(raw);
console.log(products); // → JS array or object

// Encode a JS value and write it to a .toon file
const inventory = [
  { sku: 'WDG-001', name: 'Widget A', qty: 142, price: 9.99 },
  { sku: 'WDG-002', name: 'Widget B', qty: 87,  price: 14.49 },
  { sku: 'GDG-001', name: 'Gadget X', qty: 31,  price: 49.99 },
];
writeFileSync('./data/inventory.toon', encode(inventory, { indent: 2 }), 'utf8');
js
// --- Async (server routes, pipelines) ---
import { promises as fs } from 'fs';
import { encode, decode } from '@toon-format/toon';

// Read
async function loadReportData(filePath) {
  const raw = await fs.readFile(filePath, 'utf8');
  return decode(raw); // throws if malformed — handle upstream
}

// Write
async function saveSnapshot(data, filePath) {
  const toon = encode(data, { indent: 2 });
  await fs.writeFile(filePath, toon, 'utf8');
}
Brug altid 'utf8'-kodning. TOON-filer er almindelig tekst. At udelade kodningsargumentet returnerer en Buffer — decode() forventer en streng og vil kaste en typefejl, hvis den modtager en Buffer.

Validering af TOON ved systemgrænser

decode() kaster ved ugyldigt input, hvilket er den rigtige adfærd for en parser men ubelejlig ved en API-grænse eller message-queue-forbruger, hvor du har brug for et struktureret resultat, ikke en ufanget undtagelse. Løsningen er en tynd indpakning, der omdanner kastet til en returværdi. Dette er det mønster, du vil gribe efter i Express-rutehandlere, kø-behandlere og overalt ellers, hvor eksterne data kommer ind i dit system.

js
import { decode } from '@toon-format/toon';

/**
 * Safely parse a TOON string.
 * Returns { valid: true, data } on success,
 * or { valid: false, error } on failure — never throws.
 */
export function validateToon(input) {
  if (typeof input !== 'string') {
    return { valid: false, error: 'Input must be a string' };
  }
  try {
    const data = decode(input);
    return { valid: true, data };
  } catch (err) {
    return { valid: false, error: err.message };
  }
}

Brug i en Express-rute eller en kø-forbruger ser identisk ud — kald validateToon(), forgren på valid, og fortsæt enten med data eller returner 400 / dead-letter beskeden med error-strengen. Mønstret try/catch holder den kaldende kode ren og forudsigelig.

js
// Example: queue consumer
queue.process('ingest-toon', async (job) => {
  const result = validateToon(job.data.payload);
  if (!result.valid) {
    console.error('Rejecting malformed TOON:', result.error);
    return; // dead-letter, skip, or throw depending on your queue
  }
  await db.insert(result.data);
});

Bygning af en TOON-middleware til Express

express.json() parser application/json-organer og sætter resultatet på req.body. Her er det samme til application/toon. Sæt det ind før dine rutehandlere, og resten af stakken mærker aldrig forskellen.

js
import { decode } from '@toon-format/toon';

/**
 * Express middleware: parses application/toon request bodies
 * and attaches the decoded value to req.body.
 */
export function toonBodyParser(req, res, next) {
  const contentType = req.headers['content-type'] ?? '';
  if (!contentType.includes('application/toon')) {
    return next(); // not our content type, pass through
  }

  let body = '';
  req.setEncoding('utf8');
  req.on('data', (chunk) => { body += chunk; });
  req.on('end', () => {
    try {
      req.body = decode(body);
      next();
    } catch (err) {
      res.status(400).json({ error: 'Invalid TOON body', detail: err.message });
    }
  });
  req.on('error', (err) => {
    res.status(500).json({ error: 'Request stream error', detail: err.message });
  });
}

// Wire it up:
// app.use(toonBodyParser);
// app.post('/api/import', (req, res) => {
//   // req.body is already the decoded JS value
//   res.json({ received: Array.isArray(req.body) ? req.body.length : 1 });
// });

Konvertering af databaseresultater til TOON inden afsendelse til LLM

Dette er det mønster, TOON blev bygget til. Du forespørger databasen, får et array af rækker tilbage, koder til TOON og sætter det direkte ind i din prompt. LLM'en får al strukturen uden nogen af JSONs nøgle-gentagelses-overhead. Her er en realistisk rørledning, der bruger node-postgres (pg):

js
import pg from 'pg';
import { encode } from '@toon-format/toon';

const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });

async function buildOrderPrompt(customerId) {
  // Step 1: query the database
  const { rows } = await pool.query(
    `SELECT order_id, created_at, status, total_cents, item_count
       FROM orders
      WHERE customer_id = $1
      ORDER BY created_at DESC
      LIMIT 50`,
    [customerId]
  );

  if (rows.length === 0) {
    return null;
  }

  // Step 2: encode rows to TOON
  // encode() handles all quoting automatically — no pre-processing needed
  const toonData = encode(rows, { indent: 2 });

  // Step 3: build the prompt
  return [
    'Analyse the following order history for a customer support case.',
    'Data is in TOON tabular format: name[count]{col1,col2,...}: followed by one row per line.',
    '',
    toonData,
    '',
    'Summarise any patterns that suggest the customer has a recurring issue.'
  ].join('\n');
}

// Calling code:
const prompt = await buildOrderPrompt('cust_8821');
if (prompt) {
  const reply = await callLlm(prompt); // your LLM client here
  console.log(reply);
}

Det samme mønster virker for enhver SQL-klient eller ORM — Prisma, Drizzle, Knex, Sequelize — så længe din forespørgsel returnerer almindelige JS-objekter. encode() henter nøglenavnene fra den første række og bruger dem som kolonnehoveder; efterfølgende rækker skrives som kommaseparerede værdier. Et 50-rækkers resultatsæt, der ville koste ~1.500 tokens som et JSON-array, koster typisk ~600–700 tokens som TOON.

Håndtering af fejl og edge cases

Nogle ting er værd at vide, inden du sender kode til produktion:

  • LLM returnerer misformet TOON. Modeller gengiver ikke altid et format perfekt, især ikke ved første forsøg. Omgiv decode() i en try/catch (eller brug validateToon() fra ovenstående). Hvis det fejler, log det rå svar, returner en fejl til den kaldende kode, og — hvis du har brug for struktureret output pålideligt — tilføj et nyt forsøg med en eksplicit korrektionsprompt: "Dit sidste svar var ikke gyldigt TOON. Omformater det venligst."
  • Værdier, der indeholder kommaer eller kolon. TOON bruger kommaer til at adskille værdier og kolon i objektsyntaks — begge er betydningsfulde tegn. encode() opdager disse automatisk og omgiver den berørte værdi med dobbelte anførselstegn. Du behøver aldrig at forbehandle dine data; send blot rå strenge.
  • Null og undefined. encode() serialiserer null som null (bart, ikke citeret) og udelader undefined-egenskaber fuldstændigt — den samme adfærd som JSON.stringify(). Ved afkodning returneres bart null som JS null.
  • Tomme arrays. encode([]) returnerer et gyldigt tomt TOON-array. decode() round-tripper det rent. Guard opstrøms, hvis din LLM-prompt ikke bør inkludere et tomt datasæt.
  • Meget store resultatsæt. Der er ingen hård grænse i biblioteket, men LLM'er har kontekstvinduegrænser. Paginér eller LIMIT dine forespørgsler, inden du koder — 100–200 rækker er et rimeligt loft for de fleste prompts.
Valider inden du gemmer. Hvis din rørledning accepterer TOON fra en ekstern kilde (webhook, kø, API-klient) og gemmer det afkodede resultat i en database, kør altid validateToon() først. At lade en misformet payload nå dit DB-lag gør fejlretning meget sværere end at fange det ved grænsen.

Opsummering

Mønstrene i denne artikel dækker det meste af, hvad du har brug for til at integrere TOON i en reel Node.js-kodebase: fs til fil-I/O, en validateToon()-indpakning til sikker grænse-parsing, en drop-in Express-middleware til application/toon-organer, og en DB-til-prompt-rørledning, der omdanner SQL-rækker til tokeneffektivt LLM-input. Biblioteket selv — @toon-format/toon — holder sig ude af vejen: to funktioner, ingen konfiguration, kaster ved ugyldigt input. Brug TOON Validator til at tjekke output under udvikling, TOON Formatter til at inspicere kodet data, JSON til TOON til at konvertere eksisterende datasæt, inden du sætter dem ind i en prompt, og TOON til JSON, hvis du skal videregive et afkodet svar til et downstream-system, der forventer JSON.