CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-superagent

Elegant & feature rich browser / node HTTP with a fluent API

Pending
Overview
Eval results
Files

parsers-serializers.mddocs/

Parsers & Serializers

Configurable content-type parsing and serialization for different data formats, with built-in support for JSON, form data, text, and binary content, plus extensible custom parser system.

Capabilities

Global Serializers

Default serialization functions for converting request data to appropriate formats.

/**
 * Global serializer configuration object
 */
const serialize = {
  'application/json': Function,
  'application/x-www-form-urlencoded': Function
};

// Available serializers
serialize['application/json']: (obj: any) => string;
serialize['application/x-www-form-urlencoded']: (obj: object) => string;

Usage Examples:

const superagent = require('superagent');

// JSON serialization (automatic)
superagent
  .post('https://api.example.com/users')
  .send({ name: 'John', age: 30 });
// Automatically uses serialize['application/json']

// Form serialization (automatic)
superagent
  .post('https://api.example.com/form')
  .type('form')
  .send({ username: 'user', password: 'pass' });
// Automatically uses serialize['application/x-www-form-urlencoded']

// Access global serializers
console.log('JSON serializer:', superagent.serialize['application/json']);
console.log('Form serializer:', superagent.serialize['application/x-www-form-urlencoded']);

// Custom global serializer
superagent.serialize['application/xml'] = (obj) => {
  // Custom XML serialization logic
  return `<data>${JSON.stringify(obj)}</data>`;
};

Global Parsers

Default parsing functions for converting response data from various formats.

/**
 * Global parser configuration object
 */
const parse = {
  'application/json': Function,
  'application/x-www-form-urlencoded': Function,
  'text/plain': Function,
  'text/*': Function,
  'application/octet-stream': Function,
  'image/*': Function
};

// Available parsers
parse['application/json']: (res: Response, callback: Function) => void;
parse['application/x-www-form-urlencoded']: (res: Response, callback: Function) => void;
parse['text/plain']: (res: Response, callback: Function) => void;
parse.text: (res: Response, callback: Function) => void;
parse.image: (res: Response, callback: Function) => void;

Usage Examples:

// JSON parsing (automatic)
const jsonResponse = await superagent.get('https://api.example.com/data.json');
console.log('Parsed JSON:', jsonResponse.body);

// Text parsing (automatic)
const textResponse = await superagent.get('https://api.example.com/plain.txt');
console.log('Text content:', textResponse.text);

// Binary/Image parsing (automatic)
const imageResponse = await superagent.get('https://api.example.com/image.jpg');
console.log('Binary buffer:', imageResponse.body instanceof Buffer);

// Access global parsers
console.log('JSON parser:', superagent.parse['application/json']);
console.log('Text parser:', superagent.parse.text);
console.log('Image parser:', superagent.parse.image);

// Custom global parser
superagent.parse['application/xml'] = (res, callback) => {
  // Custom XML parsing logic
  const xmlData = parseXML(res.text);
  callback(null, xmlData);
};

Request-Level Serialization

Override serialization for individual requests.

/**
 * Override request serializer function
 * @param {function} fn - Custom serializer function
 * @returns {Request} Request instance for chaining
 */
Request.prototype.serialize(fn): Request;

Usage Examples:

// Custom JSON serialization with pretty printing
superagent
  .post('https://api.example.com/data')
  .serialize((obj) => JSON.stringify(obj, null, 2))
  .send({ message: 'Hello World' });

// Custom form serialization
superagent
  .post('https://api.example.com/form')
  .type('form')
  .serialize((obj) => {
    // Custom form encoding with special handling
    return Object.keys(obj)
      .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
      .join('&');
  })
  .send({ special_chars: 'hello+world&test' });

// CSV serialization
superagent
  .post('https://api.example.com/csv')
  .type('text/csv')
  .serialize((data) => {
    // Convert array of objects to CSV
    const headers = Object.keys(data[0]).join(',');
    const rows = data.map(row => Object.values(row).join(','));
    return [headers, ...rows].join('\n');
  })
  .send([
    { name: 'John', age: 30 },
    { name: 'Jane', age: 25 }
  ]);

// XML serialization
superagent
  .post('https://api.example.com/xml')
  .type('application/xml')
  .serialize((obj) => {
    function objectToXml(obj, rootName = 'data') {
      let xml = `<${rootName}>`;
      for (const [key, value] of Object.entries(obj)) {
        if (typeof value === 'object') {
          xml += objectToXml(value, key);
        } else {
          xml += `<${key}>${value}</${key}>`;
        }
      }
      xml += `</${rootName}>`;
      return xml;
    }
    return objectToXml(obj);
  })
  .send({ user: { name: 'John', age: 30 } });

Request-Level Parsing

Override parsing for individual requests.

/**
 * Override response parser function
 * @param {function} fn - Custom parser function (res, callback) => void
 * @returns {Request} Request instance for chaining
 */
Request.prototype.parse(fn): Request;

Usage Examples:

// Custom JSON parser with error handling
superagent
  .get('https://api.example.com/data')
  .parse((res, callback) => {
    try {
      const data = JSON.parse(res.text);
      callback(null, data);
    } catch (err) {
      callback(new Error('Invalid JSON: ' + err.message));
    }
  })
  .end((err, res) => {
    if (err) {
      console.error('Parse error:', err.message);
    } else {
      console.log('Parsed data:', res.body);
    }
  });

// CSV parser
superagent
  .get('https://api.example.com/data.csv')
  .parse((res, callback) => {
    const lines = res.text.split('\n');
    const headers = lines[0].split(',');
    const data = lines.slice(1).map(line => {
      const values = line.split(',');
      const obj = {};
      headers.forEach((header, index) => {
        obj[header.trim()] = values[index]?.trim();
      });
      return obj;
    });
    callback(null, data);
  })
  .end((err, res) => {
    console.log('CSV data:', res.body);
  });

// XML parser
superagent
  .get('https://api.example.com/data.xml')
  .parse((res, callback) => {
    // Simple XML parsing (in practice, use a proper XML parser)
    const xmlData = {};
    const matches = res.text.match(/<(\w+)>([^<]+)<\/\1>/g);
    if (matches) {
      matches.forEach(match => {
        const [, key, value] = match.match(/<(\w+)>([^<]+)<\/\1>/);
        xmlData[key] = value;
      });
    }
    callback(null, xmlData);
  });

// Binary data parser with validation
superagent
  .get('https://api.example.com/file.pdf')
  .parse((res, callback) => {
    const buffer = Buffer.from(res.body);
    
    // Validate PDF header
    if (buffer.slice(0, 4).toString() !== '%PDF') {
      return callback(new Error('Invalid PDF file'));
    }
    
    callback(null, {
      type: 'pdf',
      size: buffer.length,
      buffer: buffer
    });
  });

// Custom text parser with encoding detection
superagent
  .get('https://api.example.com/text')
  .parse((res, callback) => {
    let text = res.text;
    
    // Handle different encodings or formats
    if (res.charset === 'iso-8859-1') {
      // Convert from Latin-1 to UTF-8
      text = Buffer.from(text, 'latin1').toString('utf8');
    }
    
    // Additional text processing
    const processedText = text
      .replace(/\r\n/g, '\n')  // Normalize line endings
      .trim();                 // Remove whitespace
    
    callback(null, processedText);
  });

Content-Type Detection

Automatic parser selection based on response content type.

// Automatic parser selection examples

// JSON response - uses parse['application/json']
const jsonResponse = await superagent.get('https://api.example.com/users');
// Content-Type: application/json
console.log(typeof jsonResponse.body); // 'object'

// Text response - uses parse.text
const textResponse = await superagent.get('https://api.example.com/readme.txt');
// Content-Type: text/plain
console.log(typeof textResponse.body); // 'string'

// Binary response - uses parse.image
const binaryResponse = await superagent.get('https://api.example.com/file.pdf');
// Content-Type: application/pdf
console.log(binaryResponse.body instanceof Buffer); // true

// Form response - uses parse['application/x-www-form-urlencoded']
const formResponse = await superagent.get('https://api.example.com/form-data');
// Content-Type: application/x-www-form-urlencoded
console.log(typeof formResponse.body); // 'object'

Buffering Configuration

Configure response buffering behavior for different content types.

/**
 * Global buffer configuration object
 */
const buffer = {};

// Configure buffering for specific content types
buffer['application/json'] = boolean;
buffer['text/plain'] = boolean;
buffer['image/*'] = boolean;

Usage Examples:

// Configure global buffering
superagent.buffer['application/json'] = true;   // Buffer JSON responses
superagent.buffer['text/plain'] = true;         // Buffer text responses
superagent.buffer['image/jpeg'] = false;        // Don't buffer images

// Request-level buffering override
superagent
  .get('https://api.example.com/large-json')
  .buffer(false) // Don't buffer this large response
  .parse((res, callback) => {
    // Handle streaming response
    let data = '';
    res.on('data', chunk => data += chunk);
    res.on('end', () => {
      try {
        const parsed = JSON.parse(data);
        callback(null, parsed);
      } catch (err) {
        callback(err);
      }
    });
  });

Advanced Parser Patterns

Complex parsing scenarios and patterns.

// Multi-format parser based on content
superagent
  .get('https://api.example.com/dynamic-format')
  .parse((res, callback) => {
    const contentType = res.headers['content-type'] || '';
    
    if (contentType.includes('application/json')) {
      try {
        callback(null, JSON.parse(res.text));
      } catch (err) {
        callback(err);
      }
    } else if (contentType.includes('application/xml')) {
      // Parse XML
      callback(null, parseXML(res.text));
    } else if (contentType.includes('text/csv')) {
      // Parse CSV
      callback(null, parseCSV(res.text));
    } else {
      // Default to text
      callback(null, res.text);
    }
  });

// Streaming parser for large responses
superagent
  .get('https://api.example.com/large-dataset')
  .buffer(false)
  .parse((res, callback) => {
    const results = [];
    let buffer = '';
    
    res.on('data', (chunk) => {
      buffer += chunk;
      
      // Process complete JSON objects (newline-delimited JSON)
      const lines = buffer.split('\n');
      buffer = lines.pop(); // Keep incomplete line
      
      lines.forEach(line => {
        if (line.trim()) {
          try {
            results.push(JSON.parse(line));
          } catch (err) {
            // Skip invalid lines
          }
        }
      });
    });
    
    res.on('end', () => {
      // Process remaining buffer
      if (buffer.trim()) {
        try {
          results.push(JSON.parse(buffer));
        } catch (err) {
          // Ignore final parse error
        }
      }
      callback(null, results);
    });
    
    res.on('error', callback);
  });

// Conditional parser with fallback
superagent
  .get('https://api.example.com/data')
  .parse((res, callback) => {
    // Try JSON first
    try {
      const jsonData = JSON.parse(res.text);
      callback(null, { format: 'json', data: jsonData });
      return;
    } catch (jsonErr) {
      // Fall back to form data
      try {
        const formData = parseFormData(res.text);
        callback(null, { format: 'form', data: formData });
        return;
      } catch (formErr) {
        // Fall back to plain text
        callback(null, { format: 'text', data: res.text });
      }
    }
  });

Error Handling in Parsers

Handle parsing errors gracefully.

// Robust JSON parser with error recovery
superagent
  .get('https://api.example.com/potentially-invalid-json')
  .parse((res, callback) => {
    try {
      const data = JSON.parse(res.text);
      callback(null, data);
    } catch (err) {
      // Attempt to fix common JSON issues
      let fixedText = res.text
        .replace(/,\s*}/g, '}')     // Remove trailing commas
        .replace(/,\s*]/g, ']')     // Remove trailing commas in arrays
        .replace(/'/g, '"');        // Replace single quotes with double quotes
      
      try {
        const fixedData = JSON.parse(fixedText);
        console.warn('JSON was malformed but successfully repaired');
        callback(null, fixedData);
      } catch (secondErr) {
        // If repair fails, return original error with more context
        const error = new Error(`JSON parsing failed: ${err.message}`);
        error.originalText = res.text;
        error.responseStatus = res.status;
        callback(error);
      }
    }
  });

// Parser with validation
superagent
  .get('https://api.example.com/api-data')
  .parse((res, callback) => {
    try {
      const data = JSON.parse(res.text);
      
      // Validate expected structure
      if (!data.hasOwnProperty('success')) {
        return callback(new Error('Missing required field: success'));
      }
      
      if (!Array.isArray(data.items)) {
        return callback(new Error('Expected items to be an array'));
      }
      
      callback(null, data);
    } catch (err) {
      callback(err);
    }
  });

Install with Tessl CLI

npx tessl i tessl/npm-superagent

docs

agent-sessions.md

auth-security.md

file-uploads.md

http-methods.md

index.md

parsers-serializers.md

request-building.md

request-execution.md

response-handling.md

tile.json