Skip to content
v1.0.0-beta.8

S3 Service

Harpia includes a first-class service for interacting with Amazon S3 (or compatible storage providers like MinIO and DigitalOcean Spaces) using Bun’s built-in S3Client. The S3Service provides a convenient API for file uploads, downloads, streaming, deletion, and presigned URLs.

ℹ️ This is powered by Bun’s native S3Client, meaning it does not require any external AWS SDK. When using non-AWS providers (like MinIO or DigitalOcean Spaces), make sure to set a custom endpoint URL.


Quickstart

Here are some common use cases to get started quickly:

// Assume S3Service is already initialized
// Upload
await s3.send("notes/hello.txt", "Hello, world!", { type: "text/plain" });
// Read as text
const content = await s3.readAsText("notes/hello.txt");
// Read as JSON
const json = await s3.readAsJson<{ title: string }>("data/post.json");
// Delete
await s3.delete("notes/hello.txt");
// Upload large file
await s3.sendLargeFile("videos/big.mp4", buffer, { partSize: 5 * 1024 * 1024 });
// Generate presigned URL
const url = s3.generatePresignedUrl("notes/hello.txt", { expiresIn: 3600 });

Configuration

To use the S3Service, you must configure it with your S3 credentials:

import { S3Service } from "app/services/s3";
const s3 = new S3Service({
bucket: "my-bucket",
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: "us-east-1",
endpoint: "https://s3.amazonaws.com", // optional for AWS
});

Uploading a File

await s3.send("uploads/image.png", fileBuffer, {
type: "image/png",
acl: "public-read",
});

Reading Files

As text:

const text = await s3.readAsText("docs/readme.txt");

As JSON:

const json = await s3.readAsJson<{ name: string }>("data/user.json");

As binary:

const buffer = await s3.readAsArrayBuffer("archive.zip");

Partial content:

const excerpt = await s3.readPartial("bigfile.txt", 0, 100);

Deleting Files

await s3.delete("uploads/old-image.jpg");

Uploading Large Files

Use sendLargeFile() to stream large content in parts:

await s3.sendLargeFile("videos/long.mp4", videoBuffer, {
partSize: 10 * 1024 * 1024, // 10MB chunks
queueSize: 5,
acl: "private",
});

Generating a Presigned URL

const url = s3.generatePresignedUrl("uploads/photo.jpg", {
expiresIn: 3600, // 1 hour
acl: "public-read",
method: "GET",
});

This URL can be shared with clients to securely access files without exposing sensitive credentials.


File References

The .file(key) method returns a S3File reference:

const file = s3.file("docs/manual.pdf");
await file.exists();
await file.delete();
await file.write("Hello World");

Useful when you need finer control using the underlying Bun S3File API.


Error Handling

If credentials are missing or invalid, an error with code ERR_S3_MISSING_CREDENTIALS will be thrown:

try {
await s3.send("path", buffer);
} catch (error) {
if ((error as any).code === "ERR_S3_MISSING_CREDENTIALS") {
// Handle credential error
}
}

When to Use

The S3Service is a great fit when:

  • You want to upload or read files from S3 without external dependencies
  • You need to stream large files efficiently
  • You require secure access via presigned URLs
  • You want native S3 support with excellent performance and simplicity