HttpClientRequest.bodyFile() reads a file from the local filesystem and streams it as the request body. This is the most straightforward way to upload a file in a Node.js environment.
import { FetchHttpClient, HttpClient, HttpClientRequest,} from "effect/unstable/http";import { NodeFileSystem } from "@effect/platform-node";import { Effect } from "effect";
function uploadFile(filePath: string) { return Effect.gen(function* () { const client = yield* HttpClient.HttpClient;
// bodyFile reads the file and streams it as the request body const request = yield* HttpClientRequest.post( "https://httpbin.org/post", ).pipe( HttpClientRequest.bodyFile(filePath, { contentType: "text/plain", }), );
const response = yield* client.execute(request); const data = yield* response.json;
return data; }).pipe( Effect.provide(FetchHttpClient.layer), // bodyFile requires the FileSystem service Effect.provide(NodeFileSystem.layer), );}
// Test it (create a test file first)import { writeFileSync } from "node:fs";writeFileSync("test-upload.txt", "Hello from a file!");
Effect.runPromise(uploadFile("test-upload.txt")).then((data) => { console.log("Body received:", data.data);});Output:
Body received: Hello from a file!bodyFile returns an Effect (not a plain request) because reading from the filesystem is an effectful operation that can fail. This is why we need yield* to unwrap it.
It also requires the FileSystem service in the environment, which is why we provide NodeFileSystem.layer.
The contentType option tells the server what kind of file you’re sending. If you omit it, the client will try to detect the content type from the file extension.
Under the hood, bodyFile streams the file rather than loading it entirely into memory. This means it works well with large files. Only a small buffer is held in memory at any time.