This guide helps you select the appropriate API for your CSV parsing needs in web-csv-toolbox.
Note for Bundler Users: When using Worker-based engines (e.g.,
EnginePresets.responsive()), you must specify theworkerURLoption with bundlers. See How to Use with Bundlers.
What type of input do you have?
│
├─ Learning/Prototyping?
│ └─ Use parse() (high-level API)
│
├─ String (CSV text)
│ └─ Use parseString() ✅
│
├─ ReadableStream<string> (text stream)
│ └─ Use parseStringStream() ✅
│
├─ Uint8Array or ArrayBuffer (binary data)
│ └─ Use parseBinary() ✅
│
├─ ReadableStream<Uint8Array> (binary stream)
│ └─ Use parseBinaryStream() ✅
│
├─ Response (fetch result)
│ └─ Use parseResponse() ✅
│
├─ Request (server-side)
│ └─ Use parseRequest() ✅
│
└─ Blob or File (file upload, drag-and-drop)
├─ Generic Blob: Use parseBlob() ✅
└─ File with error tracking: Use parseFile() ✅
Platform Note:
Blobis supported in all environments.Filesupport varies:
- ✅ Browsers, Node.js 20+, Deno: Full support
- ⚠️ Cloudflare Workers:
Fileconstructor unavailable (useparseBlob()withsourceoption)- ✅ FormData-sourced Files: Supported everywhere
When to use:
API:
parse() - Universal CSV parser with automatic input detectionTrade-off:
When to use:
APIs:
parseString() - Parse CSV stringparseStringStream() - Parse text streamparseBinary() - Parse binary data (Uint8Array/ArrayBuffer)parseBinaryStream() - Parse binary streamparseResponse() - Parse HTTP ResponseparseRequest() - Parse HTTP Request (server-side)parseBlob() - Parse Blob or FileparseFile() - Parse File with automatic error source trackingTrade-off:
The low-level APIs follow a 3-tier architecture providing progressive complexity:
When to use:
APIs:
String Parsing:
createStringCSVParser(options?) - Factory function for creating format-specific parsers
FlexibleStringObjectCSVParser (default) or FlexibleStringArrayCSVParserCSVProcessingOptions only (no engine option - low-level API)FlexibleStringObjectCSVParser - Always outputs object recordsFlexibleStringArrayCSVParser - Always outputs array recordsStringCSVParserStream - TransformStream for string parsingBinary Parsing:
createBinaryCSVParser(options?) - Factory function for creating format-specific binary parsers
FlexibleBinaryObjectCSVParser (default) or FlexibleBinaryArrayCSVParserBinaryCSVProcessingOptions only (no engine option - low-level API)FlexibleBinaryObjectCSVParser - Always outputs object recordsFlexibleBinaryArrayCSVParser - Always outputs array recordsBinaryCSVParserStream - TransformStream for binary parsing with multi-byte character supportBenefits:
{ stream: true } optionTrade-off:
When to use:
APIs:
FlexibleStringCSVLexer / CSVLexerTransformer - Tokenization (direct class or streaming Transform)createCSVRecordAssembler() (factory), FlexibleCSVObjectRecordAssembler, FlexibleCSVArrayRecordAssembler / CSVRecordAssemblerTransformer - Record assembly with control over record shapeTrade-off:
| Feature | parse() |
Specialized APIs (parseString(), parseBlob(), etc.) |
|---|---|---|
| Input types | All (auto-detect) | Single type |
| Performance | Good | Better (no type detection) |
| Type safety | Lower | Higher |
| Use case | Learning, prototyping | Production |
| Type detection overhead | Yes (small) | No |
Recommendation: Use specialized APIs (parseString, parseBlob, etc.) in production for better performance and type safety.
| Feature | parseBlob() |
parse() |
|---|---|---|
| Input type | Blob only |
Any (auto-detect) |
| Charset detection | Automatic from type |
Automatic (when Blob) |
| Type detection | None | Yes (slight overhead) |
| Performance | Optimal | Slightly slower |
| Use case | Production (file inputs) | Learning, prototyping |
Recommendation: Use parseBlob() in production when working with file inputs or drag-and-drop.
| Feature | parseBlob() |
parseBinary() |
|---|---|---|
| Input type | Blob |
Uint8Array / ArrayBuffer |
| Charset detection | Automatic from type |
Manual specification required |
| Use case | File inputs, drag-and-drop | In-memory binary data |
| Typical scenario | User file uploads | Downloaded/generated binary data |
Recommendation: Use parseBlob() for file inputs. Use parseBinary() when working with binary data that's already in memory.
| Feature | Streaming (default) | toArray() |
|---|---|---|
| Memory usage | O(1) - constant per record | O(n) - proportional to file size |
| Processing | Record-by-record | All records at once |
| Best for | Large files (>10MB) | Small files (<10MB) |
| Use case | Memory-efficient processing | Need all data upfront |
Recommendation: Always use streaming for large files to minimize memory usage. Use toArray() only when you need all records in memory at once.
// Streaming (memory-efficient)
for await (const record of parseString(csv)) {
await processRecord(record);
}
// toArray() (loads all into memory)
const records = await toArray(parseString(csv));
console.log(`Total: ${records.length} records`);
| Approach | UI Blocking | Communication Overhead | Stability | Best For |
|---|---|---|---|---|
| Main thread | ✅ Yes | ❌ None | ⭐ Most Stable | Server-side, blocking acceptable |
| Worker | ❌ No | ⚠️ Yes (data transfer) | ✅ Stable | Browser, non-blocking required |
| WASM (main) | ✅ Yes | ❌ None | ✅ Stable | Server-side, UTF-8 only |
| Worker + WASM | ❌ No | ⚠️ Yes (data transfer) | ✅ Stable | Browser, non-blocking, UTF-8 only |
Recommendation:
EnginePresets.responsive()) for non-blocking UIInput: string | Uint8Array | ArrayBuffer | ReadableStream | Response
Use Cases:
import { parse } from 'web-csv-toolbox';
// ✅ Good for: Learning and prototyping
const csv = 'name,age\nAlice,30';
for await (const record of parse(csv)) {
console.log(record);
}
// ✅ Good for: Input type varies at runtime
async function processCSV(input: string | Response) {
for await (const record of parse(input)) {
console.log(record);
}
}
Avoid for:
Performance:
Input: string
Use Cases:
import { parseString } from 'web-csv-toolbox';
// ✅ Perfect for: Known string input
const csv = await file.text();
for await (const record of parseString(csv)) {
console.log(record);
}
// ✅ Good for: Small to medium CSV strings
const data = `name,age\nAlice,30\nBob,25`;
for await (const record of parseString(data)) {
console.log(record);
}
Best for:
Performance:
Input: ReadableStream<string>
Use Cases:
import { parseStringStream } from 'web-csv-toolbox';
// ✅ Perfect for: Large text streams
const response = await fetch('large-data.csv');
const textStream = response.body
.pipeThrough(new TextDecoderStream());
for await (const record of parseStringStream(textStream)) {
console.log(record);
}
// ✅ Good for: Real-time data processing
const stream = getRealtimeCSVStream();
for await (const record of parseStringStream(stream)) {
await processInRealtime(record);
}
Best for:
Performance:
Input: Uint8Array | ArrayBuffer
Use Cases:
import { parseBinary } from 'web-csv-toolbox';
// ✅ Perfect for: Binary CSV data
const fileBuffer = await file.arrayBuffer();
for await (const record of parseBinary(fileBuffer, {
charset: 'utf-8'
})) {
console.log(record);
}
// ✅ Good for: Non-UTF-8 encodings
const shiftJISData = new Uint8Array([...]);
for await (const record of parseBinary(shiftJISData, {
charset: 'shift-jis'
})) {
console.log(record);
}
// ✅ Good for: Compressed data
for await (const record of parseBinary(data, {
decompression: 'gzip'
})) {
console.log(record);
}
Best for:
Performance:
Input: ReadableStream<Uint8Array>
Use Cases:
import { parseBinaryStream } from 'web-csv-toolbox';
// ✅ Perfect for: Large binary streams
const response = await fetch('large-data.csv.gz');
const binaryStream = response.body;
for await (const record of parseBinaryStream(binaryStream, {
charset: 'utf-8',
decompression: 'gzip'
})) {
console.log(record);
}
// ✅ Good for: Compressed streaming data
const stream = getCompressedCSVStream();
for await (const record of parseBinaryStream(stream, {
decompression: 'gzip'
})) {
console.log(record);
}
Best for:
Performance:
Input: Response
Use Cases:
import { parseResponse } from 'web-csv-toolbox';
// ✅ Perfect for: Fetch API responses
const response = await fetch('https://example.com/data.csv');
for await (const record of parseResponse(response)) {
console.log(record);
}
// ✅ Automatic header processing
// - Content-Type charset detection
// - Content-Encoding decompression
// - BOM handling
const response = await fetch('https://example.com/data.csv.gz');
for await (const record of parseResponse(response)) {
// Automatically decompressed and decoded
console.log(record);
}
Best for:
fetch()Performance:
Input: Request
Use Cases:
import { parseRequest } from 'web-csv-toolbox';
// ✅ Perfect for: Server-side request handling
// Cloudflare Workers
export default {
async fetch(request: Request) {
if (request.method === 'POST') {
for await (const record of parseRequest(request)) {
await database.insert(record);
}
return Response.json({ success: true });
}
}
};
// ✅ Service Workers
self.addEventListener('fetch', async (event) => {
const request = event.request;
if (request.url.endsWith('/upload-csv')) {
let count = 0;
for await (const record of parseRequest(request)) {
// Process record
count++;
}
event.respondWith(Response.json({ count }));
}
});
Best for:
Performance:
Input: Blob (including File)
Use Cases:
import { parseBlob, parseFile } from 'web-csv-toolbox';
// ✅ Perfect for: File input elements
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (event) => {
const file = event.target.files[0];
// parseFile automatically includes filename in error messages
try {
for await (const record of parseFile(file)) {
console.log(record);
}
} catch (error) {
// Error message includes: 'in "data.csv"'
console.error(error.message);
console.error('Source:', error.source); // "data.csv"
}
});
// ✅ Perfect for: Drag-and-drop
dropZone.addEventListener('drop', async (event) => {
event.preventDefault();
const file = event.dataTransfer.files[0];
for await (const record of parseFile(file)) {
console.log(record);
}
});
// ✅ Good for: Blob objects (use parseBlob)
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8' });
for await (const record of parseBlob(blob)) {
console.log(record);
}
// ✅ Edge environments: Use parseBlob with manual source
const blob = new Blob([csvData], { type: 'text/csv' });
for await (const record of parseBlob(blob, { source: 'import.csv' })) {
console.log(record);
}
Best for:
<input type="file">)parseFile() vs parseBlob():
parseFile(): Automatically sets file.name as error sourceparseBlob(): Generic Blob support, manual source specificationtype parameterPerformance:
Platform Compatibility:
parseBlob(): ✅ All environments (browsers, Workers, Node.js, Deno)parseFile(): ⚠️ May require FormData in Cloudflare Workersimport { parseFile } from 'web-csv-toolbox';
async function handleFileUpload(file: File) {
for await (const record of parseFile(file)) {
console.log(record);
}
}
Why parseFile()?
With validation and error handling:
import { parseFile, ParseError } from 'web-csv-toolbox';
async function handleFileUpload(file: File) {
// Validate file type
if (!file.type.includes('csv') && !file.name.endsWith('.csv')) {
throw new Error('Please upload a CSV file');
}
// Validate file size (10MB limit)
if (file.size > 10 * 1024 * 1024) {
throw new Error('File too large (max 10MB)');
}
try {
let count = 0;
for await (const record of parseFile(file)) {
// Process record (e.g., validate, save to database)
count++;
}
return count;
} catch (error) {
if (error instanceof ParseError) {
// Error includes filename automatically
console.error(`Parse error in "${error.source}":`, error.message);
// Example: 'Parse error in "data.csv": Field count exceeded at row 5'
throw new Error(`Invalid CSV file: ${error.message}`);
}
throw error;
}
}
import { parseResponse } from 'web-csv-toolbox';
async function fetchCSV(url: string) {
const response = await fetch(url);
for await (const record of parseResponse(response)) {
console.log(record);
}
}
Why parseResponse()?
import { parseString } from 'web-csv-toolbox';
async function parseStoredCSV(csvText: string) {
for await (const record of parseString(csvText)) {
console.log(record);
}
}
Why parseString()?
import { parseStringStream, EnginePresets } from 'web-csv-toolbox';
import { createReadStream } from 'fs';
import { Readable } from 'stream';
async function processLargeFile(filePath: string) {
const nodeStream = createReadStream(filePath, 'utf-8');
const webStream = Readable.toWeb(nodeStream) as ReadableStream<string>;
for await (const record of parseStringStream(webStream, {
engine: EnginePresets.balanced(),
})) {
await processRecord(record);
}
}
Why parseStringStream()?
import { parseStringStream } from 'web-csv-toolbox';
async function processRealtimeCSV(stream: ReadableStream<string>) {
for await (const record of parseStringStream(stream)) {
await sendToAnalytics(record);
}
}
Why parseStringStream()?
import { parseRequest } from 'web-csv-toolbox';
// Cloudflare Workers
export default {
async fetch(request: Request) {
if (request.method === 'POST' &&
request.headers.get('content-type')?.includes('text/csv')) {
const results = [];
for await (const record of parseRequest(request)) {
results.push(record);
}
return Response.json({
success: true,
count: results.length,
data: results
});
}
return new Response('Not Found', { status: 404 });
}
};
Why parseRequest()?
With validation:
import { parseRequest } from 'web-csv-toolbox';
export default {
async fetch(request: Request) {
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
const contentType = request.headers.get('content-type');
if (!contentType?.includes('text/csv')) {
return new Response('Content-Type must be text/csv', {
status: 400
});
}
let count = 0;
for await (const record of parseRequest(request, {
maxFieldCount: 1000, // Security limit
maxBufferSize: 10 * 1024 * 1024 // 10MB limit
})) {
// Process record (e.g., validate, save to database)
count++;
}
return Response.json({ success: true, count });
}
};
| API | Memory Usage | Best For |
|---|---|---|
parse() |
Depends on input type | Learning, prototyping |
parseString() |
O(n) - proportional to string size | Small to medium strings |
parseStringStream() |
O(1) - constant per record | Large streams |
parseBinary() |
O(n) - proportional to buffer size | Small to medium binary data |
parseBinaryStream() |
O(1) - constant per record | Large binary streams |
parseResponse() |
O(1) - streaming by default | HTTP responses |
Main Thread:
// Blocks main thread during parsing
for await (const record of parseString(csv)) {
console.log(record);
}
Characteristics:
Worker Thread:
// Non-blocking UI
for await (const record of parseString(csv, {
engine: { worker: true }
})) {
console.log(record);
}
Characteristics:
WASM:
// Compiled WASM code
await loadWASM();
for await (const record of parseString(csv, {
engine: { wasm: true }
})) {
console.log(record);
}
Characteristics:
Worker + WASM:
// Non-blocking execution with compiled WASM code
await loadWASM();
for await (const record of parseString(csv, {
engine: EnginePresets.responsiveFast()
})) {
console.log(record);
}
Characteristics:
Note: This preset prioritizes UI responsiveness over raw execution speed. For fastest execution time, use EnginePresets.fast() on the main thread (blocks UI).
Performance Note: Actual performance depends on many factors including CSV structure, file size, runtime environment, and system capabilities. Benchmark your specific use case to determine the best approach. See CodSpeed benchmarks for measured performance across different scenarios.
| Input Type | Server-side | Browser (blocking OK) | Browser (non-blocking) | Encoding | API |
|---|---|---|---|---|---|
string |
Main thread | Main thread | Worker | UTF-8 | parseString() |
string |
Main thread + WASM | Main thread | Worker | UTF-8 | parseString() |
string |
Main thread | Main thread | Worker | Any | parseString() |
ReadableStream<string> |
Main thread | Main thread | Worker + stream | UTF-8 | parseStringStream() |
Uint8Array |
Main thread | Main thread | Worker | UTF-8 | parseBinary() |
Uint8Array |
Main thread + WASM | Main thread | Worker | UTF-8 | parseBinary() |
Uint8Array |
Main thread | Main thread | Worker | Any | parseBinary() |
ReadableStream<Uint8Array> |
Main thread | Main thread | Main thread | Any | parseBinaryStream() |
Response |
Auto | Auto | Auto | Auto | parseResponse() |
Request |
Auto | Auto | Auto | Auto | parseRequest() |
Blob / File |
Main thread | Main thread | Main thread | Auto | parseBlob() / parseFile() |
Note: Choose execution strategy based on your requirements (blocking vs non-blocking) rather than file size alone. Benchmark your specific use case to determine the best approach.
All parsing APIs support the source option to identify the origin of CSV data in error messages:
import { parseString, parseFile, ParseError } from 'web-csv-toolbox';
// Automatic source tracking (parseFile only)
try {
for await (const record of parseFile(file)) {
// ...
}
} catch (error) {
if (error instanceof ParseError) {
console.error(error.message);
// "Field count (100001) exceeded maximum allowed count at row 5 in "data.csv""
console.error('Source:', error.source); // "data.csv"
console.error('Row:', error.rowNumber); // 5
console.error('Position:', error.position); // { line, column, offset }
}
}
// Manual source specification
try {
for await (const record of parseString(csv, { source: 'user-import.csv' })) {
// ...
}
} catch (error) {
if (error instanceof ParseError) {
console.error(error.source); // "user-import.csv"
}
}
ParseError - CSV syntax errors:
import { ParseError } from 'web-csv-toolbox';
try {
for await (const record of parseString(csv)) {
// ...
}
} catch (error) {
if (error instanceof ParseError) {
console.error('CSV syntax error:', error.message);
console.error(' Source:', error.source);
console.error(' Row:', error.rowNumber);
console.error(' Position:', error.position);
}
}
RangeError - Security limits exceeded:
try {
for await (const record of parseString(csv, {
maxFieldCount: 1000,
maxBufferSize: 10 * 1024 * 1024
})) {
// ...
}
} catch (error) {
if (error instanceof RangeError) {
if (error.message.includes('Field count')) {
console.error('Too many columns in CSV');
} else if (error.message.includes('Buffer size')) {
console.error('CSV field too large');
}
}
}
DOMException (AbortError) - Operation cancelled:
const controller = new AbortController();
try {
for await (const record of parseString(csv, {
signal: controller.signal
})) {
// ...
}
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
console.log('Parsing cancelled by user');
}
}
✅ Always use try-catch with async iteration:
try {
for await (const record of parseFile(file)) {
await processRecord(record);
}
} catch (error) {
// Handle errors
}
✅ Check error types for specific handling:
if (error instanceof ParseError) {
// CSV format error
} else if (error instanceof RangeError) {
// Security limit exceeded
} else if (error instanceof DOMException) {
// Abort or timeout
}
✅ Include context in error messages:
catch (error) {
throw new Error(`Failed to process ${file.name}: ${error.message}`);
}
❌ Don't ignore errors:
// BAD
for await (const record of parseFile(file)) {
// No error handling
}
parseResponse() for fetch() to get automatic header processingEnginePresets for execution strategies instead of manual configurationWorkerPool across multiple parse operationsloadWASM() once at startup if using WASMparse() in production when input type is knownWorkerPoolBefore (High-level):
import { parse } from 'web-csv-toolbox';
const response = await fetch('data.csv');
for await (const record of parse(response)) {
console.log(record);
}
After (Middle-level):
import { parseResponse } from 'web-csv-toolbox';
const response = await fetch('data.csv');
for await (const record of parseResponse(response)) {
console.log(record);
}
Benefits:
Quick recommendations:
parse() for simplicityparseResponse() for automatic header handlingparseRequest() for server-side request handlingparseFile() for automatic error tracking, or parseBlob() for generic Blob supportparseBlob() with manual source option (Cloudflare Workers compatibility)parseStringStream(), parseBinaryStream()){ engine: { worker: true } } or EnginePresets.responsive()) for UI responsiveness in browser applicationsparseBinary() or parseBinaryStream() with charset optionsource option or use parseFile() for automatic trackingRemember: The best API depends on your specific use case. Consider input type, encoding, execution environment (server vs browser), and blocking vs non-blocking requirements when choosing. Benchmark your actual data to make informed decisions.