Skip to content

feat: add GET /api/streams/export.csv endpoint - #661

Open
elcabasa wants to merge 1 commit into
ritik4ever:mainfrom
elcabasa:feature/api-streams-export-csv
Open

feat: add GET /api/streams/export.csv endpoint#661
elcabasa wants to merge 1 commit into
ritik4ever:mainfrom
elcabasa:feature/api-streams-export-csv

Conversation

@elcabasa

@elcabasa elcabasa commented Jul 25, 2026

Copy link
Copy Markdown

Summary
closes #601

  • Implement CSV export with streaming (res.write/res.end) at GET /api/streams/export.csv
  • Reuse existing listStreamsQuerySchema validation and filter logic
  • Extract shared applyStreamFilters() and csvEscape() helpers
  • Refactor GET /api/streams, /api/recipients/:accountId/streams, and /api/senders/:accountId/streams to use shared filter function

CSV Columns

id, sender, recipient, asset, totalAmount, vestedAmount, status, startAt, durationSeconds, createdAt

Response Headers

  • Content-Type: text/csv
  • Content-Disposition: attachment; filename="streams.csv"

Tests

6 new supertest-based tests:

  • Correct CSV headers
  • Status filtering via query param
  • Empty results
  • Valid CSV format
  • Large dataset (500 rows)
  • Validation errors (bad status, page=0, limit>100)

Pre-existing Fixes

- Implement CSV export with streaming (res.write/res.end)
- Reuse listStreamsQuerySchema validation and filter logic
- Extract shared applyStreamFilters() and csvEscape() helpers
- Refactor list, recipients, and senders routes to use shared filter
- Set Content-Type text/csv and Content-Disposition attachment headers
- CSV columns: id, sender, recipient, asset, totalAmount, vestedAmount,
  status, startAt, durationSeconds, createdAt
- Add 6 supertest-based tests (happy path, filtering, empty, format,
  large dataset, validation errors)
- Fix pre-existing duplicate declarations in validateEnv.ts and
  duplicate code block in recipients route
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

@elcabasa is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 25, 2026

Copy link
Copy Markdown

@elcabasa Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR centralizes stream filtering across listing routes, streams CSV exports incrementally with escaped fields, adds comprehensive export tests, and simplifies environment validation control flow.

Changes

Stream export and filtering

Layer / File(s) Summary
Shared stream filtering and CSV utilities
backend/src/index.ts
Adds StreamWithProgress, shared query filtering, and CSV escaping helpers.
Route filtering and CSV response integration
backend/src/index.ts
Uses shared filtering across stream, recipient, and sender routes, and writes CSV headers and rows incrementally.
Export route test coverage
backend/src/index.test.ts
Tests CSV headers, formatting, filtering, empty results, parseability, large datasets, and invalid query parameters with deterministic mocks.

Environment validation cleanup

Layer / File(s) Summary
Environment validation control flow
backend/src/config/validateEnv.ts
Simplifies the parsing failure path and reuses the existing production-mode flag.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ExportRoute
  participant Response
  Client->>ExportRoute: GET /api/streams/export.csv with query filters
  ExportRoute->>ExportRoute: filter streams and escape CSV fields
  ExportRoute->>Response: write CSV header and rows
  ExportRoute->>Response: end response
  Response-->>Client: CSV download
Loading

Possibly related PRs

Suggested reviewers: alexatsejames-alt, chkm001, emeditweb

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested streamed CSV export with the specified columns, filtering behavior, and download header.
Out of Scope Changes check ✅ Passed The additional env validation and test refactors support the export work and are not unrelated scope creep.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the CSV export endpoint.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/src/index.test.ts (2)

792-826: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unused streaming-verification helper; the one test that should use it doesn't. invokeExportCsvRoute captures write() calls incrementally, but no test calls it — the "streams large datasets without buffering" test uses request(app) and only checks the final concatenated body, so it never actually proves incremental/non-buffered writes despite its name.

  • backend/src/index.test.ts#L792-L826: either remove this helper if unneeded, or wire it into the large-dataset test below.
  • backend/src/index.test.ts#L883-L915: use invokeExportCsvRoute here and assert chunks.length > 1 (or similar) so the test actually verifies incremental streaming rather than only final content.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/index.test.ts` around lines 792 - 826, The unused
invokeExportCsvRoute helper in backend/src/index.test.ts:792-826 must be wired
into the large-dataset streaming test at backend/src/index.test.ts:883-915,
replacing request(app) there and exposing or returning write-call information so
the test asserts multiple chunks were written; no direct removal is needed at
the helper site because it becomes used by the sibling test.

869-881: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test doesn't exercise csvEscape's special-character handling.

Header/column-count checks are good, but no test data contains commas, quotes, or newlines, so the quoting/escaping branch of csvEscape (backend/src/index.ts Lines 230-236) is never exercised.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/index.test.ts` around lines 869 - 881, The CSV export test around
“produces valid CSV parseable by standard parser” must include stream data
containing commas, quotes, and newlines, then parse the response with a standard
CSV parser and assert the original field values are preserved. Ensure the
fixture reaches csvEscape and validates escaped fields rather than only
splitting lines and columns manually.
backend/src/index.ts (1)

11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused Readable import.

Readable is only referenced in the import statement, so removing it keeps the API file cleaner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/index.ts` at line 11, Remove the unused Readable import from the
module imports in index.ts, leaving the remaining imports and API implementation
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/index.ts`:
- Around line 711-717: Update the CSV export flow around applyStreamFilters to
call listStreams with query.include_archived, query.sort ?? "createdAt", and
query.order ?? "desc", matching the list, recipient, and sender routes. Preserve
the existing progress mapping and filtering behavior while ensuring export
respects validated sort/order parameters.

---

Nitpick comments:
In `@backend/src/index.test.ts`:
- Around line 792-826: The unused invokeExportCsvRoute helper in
backend/src/index.test.ts:792-826 must be wired into the large-dataset streaming
test at backend/src/index.test.ts:883-915, replacing request(app) there and
exposing or returning write-call information so the test asserts multiple chunks
were written; no direct removal is needed at the helper site because it becomes
used by the sibling test.
- Around line 869-881: The CSV export test around “produces valid CSV parseable
by standard parser” must include stream data containing commas, quotes, and
newlines, then parse the response with a standard CSV parser and assert the
original field values are preserved. Ensure the fixture reaches csvEscape and
validates escaped fields rather than only splitting lines and columns manually.

In `@backend/src/index.ts`:
- Line 11: Remove the unused Readable import from the module imports in
index.ts, leaving the remaining imports and API implementation unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 31393b17-332c-4d2b-923c-21ecf3c522c4

📥 Commits

Reviewing files that changed from the base of the PR and between b3d32c1 and 1752720.

📒 Files selected for processing (3)
  • backend/src/config/validateEnv.ts
  • backend/src/index.test.ts
  • backend/src/index.ts

Comment thread backend/src/index.ts
Comment on lines +711 to +717
const data = applyStreamFilters(
listStreams(query.include_archived).map((stream) => ({
...stream,
progress: calculateProgress(stream, now),
})),
query,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

CSV export ignores sort/order query params.

The list route (Line 605) calls listStreams(query.include_archived, query.sort ?? "createdAt", query.order ?? "desc"), and the recipient/sender routes (Lines 1030, 1081) do the same. The export route calls listStreams(query.include_archived) with no sort/order args, so it always returns createdAt-desc ordering even when a caller supplies sort/order (which the shared schema still validates). This breaks parity between list and export for the same query params.

🐛 Proposed fix
     const data = applyStreamFilters(
-      listStreams(query.include_archived).map((stream) => ({
+      listStreams(query.include_archived, query.sort ?? "createdAt", query.order ?? "desc").map((stream) => ({
         ...stream,
         progress: calculateProgress(stream, now),
       })),
       query,
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const data = applyStreamFilters(
listStreams(query.include_archived).map((stream) => ({
...stream,
progress: calculateProgress(stream, now),
})),
query,
);
const data = applyStreamFilters(
listStreams(query.include_archived, query.sort ?? "createdAt", query.order ?? "desc").map((stream) => ({
...stream,
progress: calculateProgress(stream, now),
})),
query,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/index.ts` around lines 711 - 717, Update the CSV export flow
around applyStreamFilters to call listStreams with query.include_archived,
query.sort ?? "createdAt", and query.order ?? "desc", matching the list,
recipient, and sender routes. Preserve the existing progress mapping and
filtering behavior while ensuring export respects validated sort/order
parameters.

@ritik4ever

Copy link
Copy Markdown
Owner

Hi @elcabasa,

This PR could not be merged because it has merge conflicts with the target branch.

Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged.

Thank you!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add GET /api/streams/export.csv endpoint

2 participants