Skip to content

Conversation

@nikhilkuria
Copy link
Contributor

@nikhilkuria nikhilkuria commented Nov 19, 2025

Summary

Screenshot 2025-11-19 at 19 24 00

Tested with 50 MB of data in a single data table. The download happens in <3 seconds.
If there is a need to support bigger tables, we can add streaming support later

Related Linear tickets, Github issues, and Community forum posts

Review / Merge checklist

  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with release/backport (if the PR is an urgent fix that needs to be backported)

@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team labels Nov 19, 2025
@nikhilkuria nikhilkuria changed the title feat: initial commit for csv export from data tables feat(editor): CSV download support for data tables Nov 19, 2025
@nikhilkuria nikhilkuria marked this pull request as ready for review November 19, 2025 18:32
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 7 files

Prompt for AI agents (all 2 issues)

Understand the root cause of the following 2 issues and fix them.


<file name="packages/frontend/editor-ui/src/features/core/dataTable/dataTable.store.ts">

<violation number="1" location="packages/frontend/editor-ui/src/features/core/dataTable/dataTable.store.ts:277">
Rule violated: **Tests**

`downloadDataTableCsv` introduces a new CSV export path (API call plus DOM-triggered download) but no unit/UI tests were added to cover it, violating the Community PR Guidelines §2 testing requirement for new functionality.</violation>
</file>

<file name="packages/cli/src/modules/data-table/data-table.controller.ts">

<violation number="1" location="packages/cli/src/modules/data-table/data-table.controller.ts:265">
This endpoint returns full table data but is guarded only by `dataTable:read`, letting callers who can see metadata download entire datasets without the `dataTable:readRow` permission that other row-returning routes require. Align the scope with existing row access to prevent unauthorized data export.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

return result;
};

const downloadDataTableCsv = async (dataTableId: string, projectId: string) => {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule violated: Tests

downloadDataTableCsv introduces a new CSV export path (API call plus DOM-triggered download) but no unit/UI tests were added to cover it, violating the Community PR Guidelines §2 testing requirement for new functionality.

Prompt for AI agents
Address the following comment on packages/frontend/editor-ui/src/features/core/dataTable/dataTable.store.ts at line 277:

<comment>`downloadDataTableCsv` introduces a new CSV export path (API call plus DOM-triggered download) but no unit/UI tests were added to cover it, violating the Community PR Guidelines §2 testing requirement for new functionality.</comment>

<file context>
@@ -273,6 +274,36 @@ export const useDataTableStore = defineStore(DATA_TABLE_STORE, () =&gt; {
 		return result;
 	};
 
+	const downloadDataTableCsv = async (dataTableId: string, projectId: string) =&gt; {
+		// Fetch CSV content with authentication
+		const { csvContent, filename } = await downloadDataTableCsvApi(
</file context>
Fix with Cubic

}

@Get('/:dataTableId/download-csv')
@ProjectScope('dataTable:read')
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint returns full table data but is guarded only by dataTable:read, letting callers who can see metadata download entire datasets without the dataTable:readRow permission that other row-returning routes require. Align the scope with existing row access to prevent unauthorized data export.

Prompt for AI agents
Address the following comment on packages/cli/src/modules/data-table/data-table.controller.ts at line 265:

<comment>This endpoint returns full table data but is guarded only by `dataTable:read`, letting callers who can see metadata download entire datasets without the `dataTable:readRow` permission that other row-returning routes require. Align the scope with existing row access to prevent unauthorized data export.</comment>

<file context>
@@ -261,6 +261,36 @@ export class DataTableController {
 	}
 
+	@Get(&#39;/:dataTableId/download-csv&#39;)
+	@ProjectScope(&#39;dataTable:read&#39;)
+	async downloadDataTableCsv(
+		req: AuthenticatedRequest&lt;{ projectId: string; dataTableId: string }&gt;,
</file context>
Suggested change
@ProjectScope('dataTable:read')
@ProjectScope('dataTable:readRow')
Fix with Cubic

@bundlemon
Copy link

bundlemon bot commented Nov 19, 2025

BundleMon

Unchanged files (2)
Status Path Size Limits
WASM Dependencies
tree-sitter-bash.wasm
181.26KB -
WASM Dependencies
tree-sitter.wasm
74.47KB -

No change in files bundle size

Groups updated (2)
Status Path Size Limits
**/*.js
11.34MB (+51.03KB +0.44%) -
**/*.css
227.73KB (+6.96KB +3.15%) -

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@nikhilkuria nikhilkuria requested review from a team and RicardoE105 November 19, 2025 18:54
@codecov
Copy link

codecov bot commented Nov 19, 2025

): Promise<{ csvContent: string; dataTableName: string }> {
const dataTable = await this.validateDataTableExists(dataTableId, projectId);

// Fetch columns (ordered by index)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nick: I guess we do not need all these comments?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with the comments below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude was very generous with the comments. Cleaned them out.

const actions = computed<Array<UserAction<IUser>>>(() => {
const availableActions = [
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that we added this here and not next to the search input


@Get('/:dataTableId/download-csv')
@ProjectScope('dataTable:read')
async downloadDataTableCsv(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a integration test for this endpoint? that will automatically add coverage to all the backend code (or most of it)

Comment on lines 791 to 801
const str = String(value);

// RFC 4180 compliant escaping:
// - If value contains comma, quote, or newline, wrap in quotes
// - Escape quotes by doubling them
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
return `"${str.replace(/"/g, '""')}"`;
}

return str;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like leading/trailing whitespace won't be preserved. Was this intentional? I guess we should keep the original data as it comes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Made the change so that if there is leading or trailing whitespace, the string will be in quotes. This helps import to google sheets.

return result;
};

const downloadDataTableCsv = async (dataTableId: string, projectId: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make this readable like this:

const UTF8_BOM = '\uFEFF';

const createCsvBlob = (csvContent: string): Blob => {
	// Add BOM for Excel compatibility with special characters
	return new Blob([UTF8_BOM + csvContent], { 
		type: 'text/csv;charset=utf-8;' 
	});
};

const triggerBrowserDownload = (blob: Blob, filename: string): void => {
	const url = URL.createObjectURL(blob);
	const link = document.createElement('a');
	
	link.href = url;
	link.download = filename;
	link.style.display = 'none';
	
	document.body.appendChild(link);
	
	try {
		link.click();
	} finally {
		document.body.removeChild(link);
		URL.revokeObjectURL(url);
	}
};

const downloadDataTableCsv = async (dataTableId: string, projectId: string) => {
	const { csvContent, filename } = await downloadDataTableCsvApi(
		rootStore.restApiContext,
		dataTableId,
		projectId,
	);
	
	const csvBlob = createCsvBlob(csvContent);
	triggerBrowserDownload(csvBlob, filename);
};

@currents-bot
Copy link

currents-bot bot commented Nov 20, 2025

E2E Tests: n8n tests passed after 8m 57.9s

🟢 73 · 🔴 0 · ⚪️ 2 · 🟣 1

View Run Details

Run Details

  • Project: n8n

  • Groups: 2

  • Framework: Playwright

  • Run Status: Passed

  • Commit: 3b86f72

  • Spec files: 96

  • Overall tests: 600

  • Duration: 8m 57.9s

  • Parallelization: 1

Groups

GroupId Results Spec Files Progress
ui 🟢 73 · 🔴 0 · ⚪️ 2 · 🟣 1 6 / 88
ui:isolated 🟢 0 · 🔴 0 · ⚪️ 0 0 / 8


This message was posted automatically by currents.dev | Integration Settings

@blacksmith-sh
Copy link

blacksmith-sh bot commented Nov 20, 2025

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
src/modules/data-table/tests/data-table-csv.controller.integration.test.ts/
data-table-csv.controller.integration.test
View Logs
src/modules/data-table/tests/data-table-csv.controller.integration.test.ts/
data-table-csv.controller.integration.test
View Logs
src/modules/data-table/tests/data-table-csv.controller.integration.test.ts/
data-table-csv.controller.integration.test
View Logs


Fix in Cursor

Copy link
Contributor

@RicardoE105 RicardoE105 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants