How to Convert CSV to PDF with JavaScript in NodeJS
This NodeJS tutorial uses JavaScript to convert CSV into a formatted, tagged PDF with the pdfRest Convert to PDF API Tool. The example controls table columns, alignment, borders, header treatment, row striping, padding, and multipage behavior.
Why Convert CSV to PDF with JavaScript in NodeJS?
SaaS applications frequently expose analytics and account data as CSV, which is useful for further computation but less effective as a finished report. Users who simply need to read, present, or approve the information may prefer a PDF that already communicates the table’s structure.
A usage dashboard provides one concrete scenario. Its NodeJS backend can export plan names, usage descriptions, and totals to CSV, then create a PDF for a quarterly account review. Wider descriptive columns prevent cramped text, right alignment clarifies totals, and alternating row colors help readers track values across a dense table.
By defining the layout in the API request, the product can offer a consistent PDF export without rendering a hidden web page or depending on a user’s spreadsheet settings. Header repetition also keeps long datasets usable after pagination.
JavaScript Code Example for Converting CSV to PDF
var axios = require("axios");
var fs = require("fs");
var FormData = require("form-data");
// By default, we use the US-based API service. This is the primary endpoint for global use.
var apiUrl = "https://api.pdfrest.com";
/* For GDPR compliance and enhanced performance for European users, you can switch to the EU-based service by uncommenting the URL below.
* For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
*/
//var apiUrl = "https://eu-api.pdfrest.com";
// This sample converts CSV input to a tagged PDF through multipart /pdf.
// It demonstrates structured_text_options and the format-specific conversion options.
var inputPath = "/path/to/sample.csv";
var form = new FormData();
form.append("file", fs.createReadStream(inputPath));
form.append("structured_text_options", JSON.stringify({"title":"Structured Content Sample","language":"en-US","enable_tagging":true,"page_setup":{"size":"Letter","orientation":"portrait","margin":{"top":36,"right":42,"bottom":36,"left":42}},"style":{"font":"Arial","heading_font":"Arial","code_font":"Courier","text_size":11,"text_color_rgb":[34,34,34],"heading_scale":1.35,"table":{"column_width_weights":[2,3,2],"keep_header_with_first_row":true,"repeat_headers_on_overflow":true,"show_borders":true,"border_width":0.75,"border_color_rgb":[180,188,200],"header_fill_color_rgb":[33,64,98],"header_text_color_rgb":[255,255,255],"row_fill_color_rgb":[250,250,252],"alternate_row_fill_color_rgb":[235,240,246],"cell_padding":{"top":6,"right":8,"bottom":6,"left":8}}},"csv":{"first_row_is_header":true,"delimiter":",","columns":[{"index":0,"width_weight":2,"text_align":"left"},{"index":1,"width_weight":3,"text_align":"left"},{"index":2,"width_weight":1,"text_align":"right"}]}}));
form.append("output", "pdf_from_csv");
axios.post(apiUrl + "/pdf", form, { headers: { "Api-Key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", ...form.getHeaders() }, maxBodyLength: Infinity })
.then((response) => { console.log(JSON.stringify(response.data, null, 2)); })
.catch((error) => { console.error(error.response ? error.response.data : error.message); process.exitCode = 1; });
Source: View the sample on GitHub
Breaking Down the Code
The NodeJS CSV walkthrough uses axios, form-data, and Node’s file-system module. Outside the samples project, install its HTTP and multipart dependencies with npm install axios form-data.
var axios = require("axios");
var fs = require("fs");
var FormData = require("form-data");
This NodeJS excerpt keeps the regional endpoint visible for the CSV workflow:
// By default, we use the US-based API service. This is the primary endpoint for global use. var apiUrl = "https://api.pdfrest.com"; /* For GDPR compliance and enhanced performance for European users, you can switch to the EU-based service by uncommenting the URL below. * For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work */ //var apiUrl = "https://eu-api.pdfrest.com";
JavaScript streams the .csv source into the form while preserving its filename. pdfRest infers CSV conversion from that extension, so structured_text_options does not need an input_format property.
// It demonstrates structured_text_options and the format-specific conversion options. var inputPath = "/path/to/sample.csv"; var form = new FormData();
The object passed through JSON.stringify is interpreted by pdfRest as structured_text_options. It sets PDF metadata, explicitly enables tagging, defines Letter portrait geometry and margins, and supplies the font and color choices used when laying out the CSV content.
{
"csv": {
"first_row_is_header": true,
"delimiter": ",",
"columns": [
{
"index": 0,
"width_weight": 2,
"text_align": "left"
},
{
"index": 1,
"width_weight": 3,
"text_align": "left"
},
{
"index": 2,
"width_weight": 1,
"text_align": "right"
}
]
}
}
first_row_is_header defaults to true; setting it to false renders every record as a body row. delimiter defaults to a comma and must contain exactly one character, so a tab or semicolon can be selected but a multi-character separator cannot.
Column indexes are zero-based. Unspecified columns default to weight 1 and left alignment. Width weights are positive relative proportions, not points or pixels, and text_align accepts only left, center, or right. An index outside the parsed CSV, a nonpositive weight, or another alignment value produces a validation error.
The per-column CSV weights take precedence over style.table.column_width_weights; the broader table list is only a fallback when CSV-specific widths are absent. The remaining table style controls borders, colors, row striping, and padding. keep_header_with_first_row prevents a header from appearing alone, and repeat_headers_on_overflow repeats it as rows continue onto later pages.
NodeJS uses FormData to append the CSV file, stringified structured_text_options, and output name. The generated form headers provide the multipart boundary for those fields.
var inputPath = "/path/to/sample.csv";
var form = new FormData();
form.append("file", fs.createReadStream(inputPath));
form.append("structured_text_options", JSON.stringify({"title":"Structured Content Sample","language":"en-US","enable_tagging":true,"page_setup":{"size":"Letter","orientation":"portrait","margin":{"top":36,"right":42,"bottom":36,"left":42}},"style":{"font":"Arial","heading_font":"Arial","code_font":"Courier","text_size":11,"text_color_rgb":[34,34,34],"heading_scale":1.35,"table":{"column_width_weights":[2,3,2],"keep_header_with_first_row":true,"repeat_headers_on_overflow":true,"show_borders":true,"border_width":0.75,"border_color_rgb":[180,188,200],"header_fill_color_rgb":[33,64,98],"header_text_color_rgb":[255,255,255],"row_fill_color_rgb":[250,250,252],"alternate_row_fill_color_rgb":[235,240,246],"cell_padding":{"top":6,"right":8,"bottom":6,"left":8}}},"csv":{"first_row_is_header":true,"delimiter":",","columns":[{"index":0,"width_weight":2,"text_align":"left"},{"index":1,"width_weight":3,"text_align":"left"},{"index":2,"width_weight":1,"text_align":"right"}]}}));
form.append("output", "pdf_from_csv");
axios.post(apiUrl + "/pdf", form, { headers: { "Api-Key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", ...form.getHeaders() }, maxBodyLength: Infinity })
.then((response) => { console.log(JSON.stringify(response.data, null, 2)); })
.catch((error) => { console.error(error.response ? error.response.data : error.message); process.exitCode = 1; });
Axios sends the CSV request to /pdf with the form headers and Api-Key. Its success path prints resource JSON, while the rejection path exposes either the API response or the client-side error.
form.append("file", fs.createReadStream(inputPath));
form.append("structured_text_options", JSON.stringify({"title":"Structured Content Sample","language":"en-US","enable_tagging":true,"page_setup":{"size":"Letter","orientation":"portrait","margin":{"top":36,"right":42,"bottom":36,"left":42}},"style":{"font":"Arial","heading_font":"Arial","code_font":"Courier","text_size":11,"text_color_rgb":[34,34,34],"heading_scale":1.35,"table":{"column_width_weights":[2,3,2],"keep_header_with_first_row":true,"repeat_headers_on_overflow":true,"show_borders":true,"border_width":0.75,"border_color_rgb":[180,188,200],"header_fill_color_rgb":[33,64,98],"header_text_color_rgb":[255,255,255],"row_fill_color_rgb":[250,250,252],"alternate_row_fill_color_rgb":[235,240,246],"cell_padding":{"top":6,"right":8,"bottom":6,"left":8}}},"csv":{"first_row_is_header":true,"delimiter":",","columns":[{"index":0,"width_weight":2,"text_align":"left"},{"index":1,"width_weight":3,"text_align":"left"},{"index":2,"width_weight":1,"text_align":"right"}]}}));
form.append("output", "pdf_from_csv");
axios.post(apiUrl + "/pdf", form, { headers: { "Api-Key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", ...form.getHeaders() }, maxBodyLength: Infinity })
.then((response) => { console.log(JSON.stringify(response.data, null, 2)); })
.catch((error) => { console.error(error.response ? error.response.data : error.message); process.exitCode = 1; });
Beyond the Tutorial
The same technique can support billing summaries, event exports, survey results, and application health reports. Applications can construct different column arrays for each dataset while retaining a shared visual style.
Tagged tables can improve structural interpretation for accessibility tools and later processing, though tags alone are not a conformance claim. Try representative datasets in API Lab and review the Convert to PDF reference before finalizing production options.