Skip to content

Conversation

xxRockOnxx
Copy link
Contributor

@xxRockOnxx xxRockOnxx commented Dec 21, 2022

What kind of change does this PR introduce?

Refactor? Feature?

What is the current behavior?

Related to #72

Right now the library only allows download as Blob.

What is the new behavior?

This PR allows downloading as stream.

The following code is made possible:

// Regular download (returns Blob)
const { data: blob } = await storage.from(bucket).download(path)

// Stream download (returns ReadableStream)
const { data: stream } = await storage.from(bucket).download(path).asStream()

Additional context

This is done by returning a class that either implements PromiseLike<Blob> or PromiseLike<ReadableStream>.

Thus 2 new classes were added.

I have considered adding 1 class only such that there will only be DownloadBuilder<Blob | ReadableStream> but it may cause confusion because the following will be possible (core code only for brevity):

class DownloadBuilder
{
  private _asStream = false

  asStream() {
    this._asStream = true
    return this
  }

  then() {
    // actual code
  }
}

// nonsensical chaining
const builder = storage.download('file.pdf')
const invalid = await builder.asStream().asStream() // ❌ Should not be possible or otherwise confusing

// mutation confusion
const builder = storage.download('file.pdf')
const invalid = await builder.asStream()
const blob = await builder // ❌ builder has been mutated and the actual response is a stream

Alternative approaches considered:

  • Runtime error: Throw when asStream() is called twice → pushes errors to runtime instead of compile-time
  • Return same instance: Return this when already a stream builder → still confusing

Having 2 classes simply makes this impossible to happen.

@jorgeAgoiz
Copy link

Hi @inian!! Is any change necessary for approval? I think this is a very good feature

@jasperverbeet
Copy link

Bump, this would be really useful

@mandarini
Copy link
Contributor

Hi @xxRockOnxx! 👋

Thank you for this PR - adding streaming support is a valuable feature that the community has been requesting for a while (as evidenced by the interest in #72).

After discussing this internally, we think the API could be even cleaner with a slightly different approach. Instead of using an options flag, we'd prefer one of these patterns:

Option 1: Separate methods

// Regular download (returns Blob)
const { data: blob } = await storage.from(bucket).download(path)

// Stream download (returns ReadableStream)
const { data: stream } = await storage.from(bucket).downloadAsStream(path)

Option 2: Chain method (preferred)

// Regular download (returns Blob)
const { data: blob } = await storage.from(bucket).download(path)

// Stream download (returns ReadableStream)
const { data: stream } = await storage.from(bucket).download(path).asStream()

The chain method approach (Option 2) would be our preference as it provides a clean, intuitive API that's discoverable through TypeScript autocompletion.

This would involve:

  1. Having download() return an object that includes the response
  2. Adding an asStream() method that returns the ReadableStream
  3. Maintaining backward compatibility by defaulting to Blob

Would you be willing to update your implementation to use one of these patterns?

We really appreciate your contribution and look forward to getting this feature merged! Let us know if you have any questions or need any help with the implementation.

Thanks again! 🚀

@mandarini mandarini self-assigned this Sep 5, 2025
@xxRockOnxx
Copy link
Contributor Author

@mandarini the preferred changes has been implemented.

I will edit the OP to reflect this change.

@supabase supabase deleted a comment from github-actions bot Sep 11, 2025
@mandarini
Copy link
Contributor

Hi @xxRockOnxx! Thanks for implementing the fluent API pattern as requested! The API design looks great and aligns perfectly with what we discussed! However, it looks like the new BlobDownloadBuilder and StreamDownloadBuilder classes referenced in your code are missing from the PR. The build is currently failing due to that.
Could you please add these two builder class files to the PR? They seem to have been accidentally left out of the commit. Once those files are added, this should be ready for review!

Let me know if you need any help or have questions.

@xxRockOnxx
Copy link
Contributor Author

xxRockOnxx commented Sep 11, 2025

@mandarini sorry. i was about to sleep when i pushed that. i now added the missing files.

Also I'm not sure if "Builder" is the right word to call these because it seems unnatural to await builder so that's something I would like you to check.

If preferred, I don't mind changing it to Option 1 instead.

Copy link

🚀 Preview Release Status

false


Last updated: 2025-09-12T12:50:01Z

Copy link
Contributor

@mandarini mandarini left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution!

@mandarini
Copy link
Contributor

@xxRockOnxx oh no!! coveralls is failing :( I know it's a small percentage but can you please take one small last look at this? Sorry, I know how frustrating this must be..

@xxRockOnxx
Copy link
Contributor Author

@mandarini i now included error handling tests for stream download and it works perfectly when running npm run test locally.

I'm not sure how i can replicate the coverage test with your setup without pinging you for it.

@mandarini mandarini merged commit 891375b into supabase:main Sep 17, 2025
2 of 3 checks passed
@mandarini
Copy link
Contributor

Thank you very much for your contribution @xxRockOnxx !

@xxRockOnxx
Copy link
Contributor Author

@mandarini a little off-topic, may I send you an email for it instead?

@mandarini
Copy link
Contributor

@xxRockOnxx you can reach out on our Discord: https://discord.supabase.com/

@kiwicopple
Copy link
Member

🎉 This PR is included in version 2.12.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants