Blog

Luis Majano

October 30, 2025

Spread the word


Share your thoughts

We're thrilled to announce the release of bx-csv, a powerful premium module that brings enterprise-grade CSV processing capabilities to BoxLang+ subscribers! Built on Apache Commons CSV, this module provides both traditional functions and a modern, fluent API that makes working with CSV files a breeze.

✨ What's New?

The bx-csv module transforms how you work with CSV files in BoxLang, offering:

  • πŸš€ Modern Fluent API - Chainable, elegant code that's easy to read and maintain
  • ⚑ Streaming Support - Process massive files without memory concerns
  • 🎯 Smart Data Transformation - Built-in filtering and mapping during load operations
  • πŸ“¦ Multi-Format Support - Seamless conversion between CSV, JSON, Query, and arrays
  • 🎨 Flexible Configuration - Custom delimiters, quotes, escapes, and line separators
  • πŸ’ͺ Production Ready - Battle-tested Apache Commons CSV foundation

🎟️ Subscription & Trial Information

The bx-csv module is available exclusively to BoxLang+ and BoxLang++ subscribers. However, we want everyone to experience its power, so it comes with a generous 60-day trial when installed alongside the bx-plus module.

Ready to try it? Install it now:

box install bx-csv

Not a subscriber yet? Check out our plans and start your trial today!


πŸš€ The Modern Way: Fluent API

We've designed the fluent API to be intuitive and powerful. Here's how easy it is to create and manipulate CSV files:

Creating a CSV File

// Create a simple contacts file
CSV()
    .setHeaders( "Name", "Email", "Age" )
    .addRow( [ "John Doe", "john@example.com", 30 ] )
    .addRow( [ "Jane Smith", "jane@example.com", 25 ] )
    .addRow( [ "Bob Johnson", "bob@example.com", 35 ] )
    .save( "contacts.csv" );

That's it! Clean, readable, and maintainable. The fluent API chains methods together, making your intent crystal clear.

Loading and Transforming Data

// Load, filter, and transform in one elegant chain
CSV( "raw-data.csv" )
    .filter( ( row ) => {
        // Only include rows with valid emails
        return row[ 2 ].find( "@" ) > 0;
    } )
    .map( ( row ) => {
        // Normalize the data
        row[ 0 ] = row[ 0 ].trim().toUpperCase(); // Name
        row[ 1 ] = row[ 1 ].trim(); // Phone
        row[ 2 ] = row[ 2 ].trim().toLowerCase(); // Email
        return row;
    } )
    .save( "cleaned-data.csv" );

⚑ Streaming Large Files

One of the most powerful features is the ability to process huge files without loading them entirely into memory:

// Process a multi-gigabyte file efficiently
CSV().process( "huge-file.csv", ( row ) => {
    // Process each row individually
    queryExecute(
        "INSERT INTO customers (name, email) VALUES ( ?, ? )",
        [ row[ 1 ], row[ 2 ] ]
    );
} );

The streaming API ensures your application remains responsive even when processing files with millions of rows.

πŸ“Š Seamless Format Conversion

The bx-csv module makes it trivial to convert between different data formats:

From Database to CSV

// Export query results directly to CSV
qData = queryExecute( "
    SELECT
        customer_name,
        order_date,
        product_name,
        quantity,
        unit_price,
        ( quantity * unit_price ) AS total
    FROM orders
    WHERE order_date >= ?
", [ dateAdd( "m", -1, now() ) ] );

CSVFile.fromQuery( qData )
    .delimiter( ',' )
    .quoteMode( "MINIMAL" )
    .save( "reports/monthly-orders.csv" );

From CSV to JSON

// Convert CSV to pretty-printed JSON
csv = CSV()
    .setHeaders( "Name", "Age", "City" )
    .addRow( [ "John", 30, "New York" ] )
    .addRow( [ "Jane", 25, "Los Angeles" ] );

jsonString = csv.toJson( true );
// Returns formatted JSON array of objects

From JSON to CSV

// Import JSON data and save as CSV
jsonData = '[
    { "Product": "Widget A", "Price": 10.99, "InStock": true },
    { "Product": "Widget B", "Price": 15.99, "InStock": false }
]';

CSVFile.fromJson( jsonData ).save( "products.csv" );

🎨 Flexible Configuration

The module supports virtually any CSV format you might encounter:

// Pipe-delimited with custom quote character
CSV()
    .delimiter( '|' )
    .quote( '\'' )
    .trim( true )
    .setHeaders( "Name", "Age", "City" )
    .addRow( [ "John O'Brien", 30, "New York" ] )
    .save( "data.txt" );

// Tab-separated values (TSV)
CSV()
    .delimiter( '\t' )
    .load( "data.tsv" );

// Handle comments and empty lines
CSV()
    .commentMarker( '#' )
    .ignoreEmptyLines( true )
    .load( "data-with-comments.csv" );

πŸ’Ό Real-World Use Cases

Data Import Pipeline

// Build a robust data import system
CSV()
    .setPath( "imports/daily-feed.csv" )
    .delimiter( '|' )
    .trim( true )
    .ignoreEmptyLines( true )
    .filter( ( row ) => {
        // Validate required fields
        return row[ 1 ] != "" && row[ 2 ] != "" && row[ 3 ] > 0;
    } )
    .process( ( row ) => {
        // Insert validated rows into database
        try {
            queryExecute(
                "INSERT INTO products ( sku, name, price ) VALUES ( ?, ?, ? )",
                [ row[ 1 ], row[ 2 ], row[ 3 ] ]
            );
        } catch( any e ) {
            logError( "Failed to import row: " & row[ 1 ], e );
        }
    } );

Report Generation

// Generate monthly sales report
qSales = queryExecute( "
    SELECT
        DATE_FORMAT( order_date, '%Y-%m' ) as month,
        COUNT(*) as total_orders,
        SUM( order_total ) as revenue,
        AVG( order_total ) as avg_order
    FROM orders
    WHERE order_date >= DATE_SUB( NOW(), INTERVAL 12 MONTH )
    GROUP BY month
    ORDER BY month DESC
" );

CSVFile.fromQuery( qSales )
    .headerComments(
        "Monthly Sales Report",
        "Generated: " & now(),
        "Period: Last 12 Months"
    )
    .commentMarker( '#' )
    .quoteMode( "NON_NUMERIC" )
    .save( "reports/monthly-sales.csv" );

Data Consolidation

// Combine multiple CSV files from different sources
combined = CSV().setHeaders( "Name", "Email", "Source", "ImportDate" );

// Process each source file
sources = [
    { file: "mailchimp-export.csv", name: "MailChimp" },
    { file: "salesforce-export.csv", name: "Salesforce" },
    { file: "hubspot-export.csv", name: "HubSpot" }
];

sources.each( ( source ) => {
    CSV().process( source.file, ( row ) => {
        // Skip header rows
        if ( row[ 1 ] != "Name" && row[ 1 ] != "name" ) {
            combined.addRow( [
                row[ 1 ],
                row[ 2 ],
                source.name,
                now()
            ] );
        }
    } );
} );

combined.save( "combined-contacts.csv" );

πŸ“š Complete Documentation

The bx-csv module is fully documented in our official BoxLang documentation, and we've even created a dedicated MCP Server for it:

🎁 Get Access

bx-ldap is available exclusively to BoxLang +/++ subscribers. Join our subscription program to access this and other premium modules that extend BoxLang's capabilities:

  • Priority Support - Get help when you need it
  • Premium Modules - Access subscriber-only modules
  • Early Access - Be first to try new features
  • Exclusive Benefits - CFCasts account, FORGEBOX Pro, and more

πŸ›’ Purchase Options

Ready to unlock bx-ldap and other premium modules? Choose your plan:

🌟 View BoxLang Plans & Pricing

Need help choosing the right plan or have questions? Contact us directly:

πŸ“§ info@boxlang.io

Add Your Comment

Recent Entries

Your Development Team Is at Capacity. Here’s How to Keep Critical Work Moving!

Your Development Team Is at Capacity. Here’s How to Keep Critical Work Moving!

The problem is not that your team lacks priorities.It is that maintenance, delivery, and long-term improvement are competing for the same people.

For companies running business-critical CFML, ColdFusion, or BoxLang applications, that pressure can be especially difficult to solve. Experienced developers are not always easy to hire quickly, application knowledge may be concentrated in one or two people, and a generalist may need significant context before taking ownership of the work. A permanent hire can also require a significant investment in recruiting, compensation, onboarding, and long-term capacity even when the immediate need is a specific project, urgent maintenance, or a period of transition.

Maria Jose Herrera
Maria Jose Herrera
September 18, 2026
Copy of BoxLang AI 3.4 Blog Series Part 5 : Reasoning Without the Guesswork

Copy of BoxLang AI 3.4 Blog Series Part 5 : Reasoning Without the Guesswork

Reasoning-capable models have been usable in BoxLang AI for a while. params passes straight through to the provider body, so params: { thinking: { type: "enabled", budget_tokens: 10000 } } for Claude, or params: { reasoning_effort: "high" } for OpenAI, already reached the API. What never worked was reading the reasoning back. It was parsed out on arrival and silently dropped. In 3.4.0, that's fixed, and it's fixed the same way for every provider.

Luis Majano
Luis Majano
September 15, 2026