w

API Reference

This document provides comprehensive API reference for integrating URL encoding/decoding functionality into your applications, including JavaScript examples and implementation details.

JavaScript API

Core Functions

encodeURIComponent(string)

Standard JavaScript function for URL encoding:

// Basic encoding
const encoded = encodeURIComponent('Hello World!');
console.log(encoded); // "Hello%20World%21"

// Special characters
const special = encodeURIComponent('user@example.com');
console.log(special); // "user%40example.com"

// Unicode support
const unicode = encodeURIComponent('こんにちは');
console.log(unicode); // "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"

decodeURIComponent(string)

Standard JavaScript function for URL decoding:

// Basic decoding
const decoded = decodeURIComponent('Hello%20World%21');
console.log(decoded); // "Hello World!"

// Special characters
const email = decodeURIComponent('user%40example.com');
console.log(email); // "user@example.com"

// Unicode decoding
const japanese = decodeURIComponent('%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF');
console.log(japanese); // "こんにちは"

Enhanced URL Encoder Class

Custom implementation with additional features:

class URLEncoder {
  /**
   * Encode text with custom options
   * @param {string} text - Text to encode
   * @param {Object} options - Encoding options
   * @returns {string} Encoded text
   */
  static encode(text, options = {}) {
    const { spaceAsPlus = false, customReserved = [], preserveCase = false } = options;

    let encoded = encodeURIComponent(text);

    // Replace %20 with + if requested
    if (spaceAsPlus) {
      encoded = encoded.replace(/%20/g, '+');
    }

    // Handle custom reserved characters
    customReserved.forEach((char) => {
      const encodedChar = encodeURIComponent(char);
      encoded = encoded.replace(new RegExp(encodedChar, 'g'), char);
    });

    // Preserve case for hex digits
    if (!preserveCase) {
      encoded = encoded.toUpperCase();
    }

    return encoded;
  }

  /**
   * Decode URL-encoded text with error handling
   * @param {string} text - Text to decode
   * @param {Object} options - Decoding options
   * @returns {Object} Result object with success/error info
   */
  static decode(text, options = {}) {
    const { strictMode = true } = options;

    try {
      // Handle + as space
      const normalized = text.replace(/\+/g, '%20');
      const decoded = decodeURIComponent(normalized);

      return {
        success: true,
        result: decoded,
        error: null,
      };
    } catch (error) {
      if (strictMode) {
        return {
          success: false,
          result: null,
          error: `Decoding failed: ${error.message}`,
        };
      } else {
        // Attempt partial decoding
        return this.partialDecode(text);
      }
    }
  }

  /**
   * Attempt partial decoding for malformed input
   * @param {string} text - Malformed text
   * @returns {Object} Partial decode result
   */
  static partialDecode(text) {
    const chunks = text.split('%');
    let result = chunks[0];

    for (let i = 1; i < chunks.length; i++) {
      const chunk = chunks[i];
      if (chunk.length >= 2) {
        const hex = chunk.substring(0, 2);
        const rest = chunk.substring(2);

        try {
          const charCode = parseInt(hex, 16);
          result += String.fromCharCode(charCode) + rest;
        } catch {
          result += '%' + chunk;
        }
      } else {
        result += '%' + chunk;
      }
    }

    return {
      success: true,
      result: result,
      error: 'Partial decoding applied',
    };
  }

  /**
   * Validate URL encoding
   * @param {string} text - Text to validate
   * @returns {Object} Validation result
   */
  static validate(text) {
    const issues = [];

    // Check for malformed percent encoding
    const percentPattern = /%[0-9A-Fa-f]{2}/g;
    const invalidPercent = /%(?![0-9A-Fa-f]{2})/g;

    if (invalidPercent.test(text)) {
      issues.push('Invalid percent encoding found');
    }

    // Check for unencoded reserved characters
    const unencodedReserved = /[<>"{}|\\^`\[\]]/g;
    if (unencodedReserved.test(text)) {
      issues.push('Unencoded reserved characters found');
    }

    // Check for control characters
    const controlChars = /[\x00-\x1F\x7F]/g;
    if (controlChars.test(text)) {
      issues.push('Control characters detected');
    }

    return {
      isValid: issues.length === 0,
      issues: issues,
    };
  }
}

Batch Processing API

class BatchURLProcessor {
  /**
   * Process multiple URLs in batch
   * @param {string[]} urls - Array of URLs to process
   * @param {string} operation - 'encode' or 'decode'
   * @param {Object} options - Processing options
   * @returns {Promise<Object[]>} Array of results
   */
  static async processBatch(urls, operation, options = {}) {
    const { chunkSize = 100, onProgress = null, errorHandling = 'continue' } = options;

    const results = [];
    const totalUrls = urls.length;

    for (let i = 0; i < totalUrls; i += chunkSize) {
      const chunk = urls.slice(i, i + chunkSize);
      const chunkResults = chunk.map((url, index) => {
        try {
          const result = operation === 'encode' ? URLEncoder.encode(url) : URLEncoder.decode(url);

          return {
            index: i + index,
            input: url,
            output: result.result || result,
            success: result.success !== false,
            error: result.error || null,
          };
        } catch (error) {
          if (errorHandling === 'stop') {
            throw error;
          }

          return {
            index: i + index,
            input: url,
            output: null,
            success: false,
            error: error.message,
          };
        }
      });

      results.push(...chunkResults);

      // Report progress
      if (onProgress) {
        onProgress({
          processed: results.length,
          total: totalUrls,
          percentage: Math.round((results.length / totalUrls) * 100),
        });
      }

      // Yield control to prevent blocking
      await new Promise((resolve) => setTimeout(resolve, 0));
    }

    return results;
  }
}

Query String Utilities

Query Parameter Encoding

class QueryStringUtils {
  /**
   * Build encoded query string from object
   * @param {Object} params - Parameters object
   * @param {Object} options - Encoding options
   * @returns {string} Encoded query string
   */
  static build(params, options = {}) {
    const { arrayFormat = 'brackets', spaceAsPlus = false } = options;

    const pairs = [];

    Object.entries(params).forEach(([key, value]) => {
      if (Array.isArray(value)) {
        value.forEach((item) => {
          const encodedKey =
            arrayFormat === 'brackets' ? `${encodeURIComponent(key)}[]` : encodeURIComponent(key);
          const encodedValue = encodeURIComponent(item);
          pairs.push(`${encodedKey}=${encodedValue}`);
        });
      } else if (value !== null && value !== undefined) {
        const encodedKey = encodeURIComponent(key);
        const encodedValue = encodeURIComponent(value);
        pairs.push(`${encodedKey}=${encodedValue}`);
      }
    });

    let queryString = pairs.join('&');

    if (spaceAsPlus) {
      queryString = queryString.replace(/%20/g, '+');
    }

    return queryString;
  }

  /**
   * Parse encoded query string to object
   * @param {string} queryString - Query string to parse
   * @param {Object} options - Parsing options
   * @returns {Object} Parsed parameters
   */
  static parse(queryString, options = {}) {
    const { arrayFormat = 'brackets' } = options;
    const params = {};

    // Remove leading ? if present
    const cleanQuery = queryString.replace(/^\?/, '');

    if (!cleanQuery) return params;

    const pairs = cleanQuery.split('&');

    pairs.forEach((pair) => {
      const [key, value = ''] = pair.split('=');
      const decodedKey = decodeURIComponent(key.replace(/\+/g, ' '));
      const decodedValue = decodeURIComponent(value.replace(/\+/g, ' '));

      if (arrayFormat === 'brackets' && decodedKey.endsWith('[]')) {
        const arrayKey = decodedKey.slice(0, -2);
        if (!params[arrayKey]) params[arrayKey] = [];
        params[arrayKey].push(decodedValue);
      } else {
        if (params[decodedKey]) {
          // Convert to array if multiple values
          if (!Array.isArray(params[decodedKey])) {
            params[decodedKey] = [params[decodedKey]];
          }
          params[decodedKey].push(decodedValue);
        } else {
          params[decodedKey] = decodedValue;
        }
      }
    });

    return params;
  }
}

Error Handling

Error Types

class URLEncodingError extends Error {
  constructor(message, code, originalText) {
    super(message);
    this.name = 'URLEncodingError';
    this.code = code;
    this.originalText = originalText;
  }
}

// Error codes
const ERROR_CODES = {
  INVALID_INPUT: 'INVALID_INPUT',
  MALFORMED_ENCODING: 'MALFORMED_ENCODING',
  UNICODE_ERROR: 'UNICODE_ERROR',
  SIZE_LIMIT_EXCEEDED: 'SIZE_LIMIT_EXCEEDED',
};

Safe Encoding Wrapper

function safeEncode(text, options = {}) {
  try {
    if (typeof text !== 'string') {
      throw new URLEncodingError('Input must be a string', ERROR_CODES.INVALID_INPUT, text);
    }

    if (text.length > (options.maxLength || 10000)) {
      throw new URLEncodingError(
        'Input exceeds maximum length',
        ERROR_CODES.SIZE_LIMIT_EXCEEDED,
        text,
      );
    }

    return {
      success: true,
      result: URLEncoder.encode(text, options),
      error: null,
    };
  } catch (error) {
    return {
      success: false,
      result: null,
      error: error.message,
    };
  }
}

Browser Compatibility

Feature Detection

function checkURLEncodingSupport() {
  const features = {
    encodeURIComponent: typeof encodeURIComponent === 'function',
    decodeURIComponent: typeof decodeURIComponent === 'function',
    unicode: true,
    performance: typeof performance !== 'undefined',
  };

  // Test Unicode support
  try {
    const test = encodeURIComponent('🌟');
    features.unicode = test === '%F0%9F%8C%9F';
  } catch {
    features.unicode = false;
  }

  return features;
}

Polyfills

// Polyfill for older browsers
if (!window.encodeURIComponent) {
  window.encodeURIComponent = function (str) {
    return escape(str).replace(/\+/g, '%2B');
  };
}

if (!window.decodeURIComponent) {
  window.decodeURIComponent = function (str) {
    return unescape(str.replace(/\+/g, ' '));
  };
}

Usage Examples

Real-world Integration

// Express.js middleware example
function urlEncodingMiddleware(req, res, next) {
  req.safeQuery = {};

  Object.entries(req.query).forEach(([key, value]) => {
    try {
      req.safeQuery[key] = decodeURIComponent(value);
    } catch (error) {
      req.safeQuery[key] = value; // Keep original if decode fails
    }
  });

  next();
}

// React component example
function URLInput({ onEncode, onDecode }) {
  const [input, setInput] = useState('');
  const [output, setOutput] = useState('');

  const handleEncode = () => {
    const result = safeEncode(input);
    if (result.success) {
      setOutput(result.result);
      onEncode?.(result.result);
    } else {
      console.error('Encoding failed:', result.error);
    }
  };

  const handleDecode = () => {
    const result = URLEncoder.decode(input);
    if (result.success) {
      setOutput(result.result);
      onDecode?.(result.result);
    } else {
      console.error('Decoding failed:', result.error);
    }
  };

  return (
    <div>
      <textarea
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Enter text to encode/decode"
      />
      <button onClick={handleEncode}>Encode</button>
      <button onClick={handleDecode}>Decode</button>
      <textarea value={output} readOnly placeholder="Result will appear here" />
    </div>
  );
}

This API reference provides comprehensive guidance for implementing URL encoding/decoding functionality in various JavaScript environments and frameworks.

Was this page helpful?