Send file through fetch in Bun
Bun supports web standard fetch API for working with remote resources. The fetch API is a pretty common & versatile API used in browsers. The fetch API supports web’s Request and Response interfaces. A request object is created with all the data and given to fetch API. The fetch API makes the HTTP request, and returns a response object to the caller. A great documentation of fetch API is available here.
One of the common use case of the fetch API is to upload files. The Request object supports file upload via Blob or web’s ReadableStream. Bun natively supports both Blob and readable streams. This makes it pretty easy to upload files through fetch in Bun. In this brief article, we’ll go over how to do file uploads through fetch API in Bun.
To upload a file to a remote resource, like a file upload server, we’ll be using two APIs:
- Bun.file
- fetch
Step 1
The Bun.file API opens a file and returns a readable stream associated with the opened file. This readable stream is the web standard readable stream. Consider the following code:
const file = Bun.file('./public/sample.pdf');
The file object is of type Bun.FileBlob which extends Blob:
export interface FileBlob extends Blob {
slice(begin?: number, end?: number): FileBlob;
writer(): FileSink;
readonly readable: ReadableStream;
}
The FileBlob has a readable stream that’s associated with the opened file.
Step 2
The FileBlob from the step 1 can not be directly used as the body of the fetch API (at the time of writing, might get fixed in future). Despite multiple tries, the stream never worked with the fetch API body. Due to this, we need to fall back to reading the file as a buffer and then pass the buffer to the fetch API. This has a memory impact, but it works for now. Consider the following updated code, which uploads a PDF file to the public beeceptor server:
const f = Bun.file("./public/sample.pdf");
const req = new Request({
url: "https://buntest.free.beeceptor.com",
method: "POST",
body: await f.arrayBuffer(),
headers: {
"content-type": "application/pdf",
},
});
const res = await fetch(req);
console.log(res.ok, res.status);
On running the code, we can see the file getting uploaded to the beeceptor server:
The request headers are:
The upload works fine.
However, this should have worked seamlessly with streams too. Let us know if you can find a way to make it work.