PAS7 Studio
Illustration of a Bun.js, S3-compatible storage, and Sharp pipeline
Technology27 Aug 2026·4 min read·Updated 27 Aug 2026

Bun.js + S3 + Sharp: an image upload and optimization pipeline

Build a secure Bun.js image pipeline with presigned S3 URLs, Sharp resizing, WebP variants, and storage-safe validation.

Full-stack developersMedia platform teamsTeams building upload APIs

Bun validates the requested file and returns a short-lived presigned PUT URL. The browser uploads directly to S3-compatible storage. A worker reads the original, creates WebP and thumbnail variants with Sharp, and publishes only verified objects.

Large files do not pass through the API process.
The browser receives no AWS credentials.
Sharp normalizes format, dimensions, and quality before publishing.
Xin

Architecture without proxying file bytes

Bun is the control plane; object storage is the data plane.

01

Prepare upload

The client sends filename, MIME type, and size. The API checks the allowlist and returns a key plus presigned URL.

02

Upload directly

The browser performs PUT to S3. Your Bun process does not hold the file in memory.

03

Process

A worker reads the original and Sharp creates a thumbnail and WebP preview.

04

Publish

Store only verified keys, dimensions, MIME type, and processing status in the database.

The file goes directly to storage, so Bun handles access control and metadata instead of proxying large bytes.

Section architecture screenshot

Step 1: install the SDK and Sharp

01

Add dependencies

BASH
bun add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner sharp

Sharp has native components; verify optional dependencies for the target Docker platform.

02

Set the bucket

ENV
S3_REGION=eu-central-1
S3_BUCKET=media
S3_ENDPOINT=https://s3.example.com
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...

S3_ENDPOINT also supports R2 and MinIO. Never expose the secret key to the frontend.

Step 2: issue a presigned PUT URL

Keep the URL short-lived, use a random object key, and bind it to the expected Content-Type. Verify the actual object size after upload.

TS
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: Bun.env.S3_REGION!, endpoint: Bun.env.S3_ENDPOINT || undefined, forcePathStyle: Boolean(Bun.env.S3_ENDPOINT) });
const allowed = new Set(["image/jpeg", "image/png", "image/webp"]);
export async function createUploadUrl(contentType: string) {
  if (!allowed.has(contentType)) throw new Error("Unsupported image type");
  const key = `originals/${crypto.randomUUID()}`;
  const command = new PutObjectCommand({ Bucket: Bun.env.S3_BUCKET!, Key: key, ContentType: contentType, ServerSideEncryption: "AES256" });
  return { key, url: await getSignedUrl(s3, command, { expiresIn: 300 }) };
}

Step 3: create variants with Sharp

Do not trust the file extension. Decode the file server-side and reject invalid or oversized images before publishing.

TS
import sharp from "sharp";
export async function makeVariants(input: Buffer) {
  const image = sharp(input, { limitInputPixels: 40_000_000 });
  const metadata = await image.metadata();
  if (!metadata.width || !metadata.height) throw new Error("Invalid image");
  const thumbnail = await image.clone().rotate().resize({ width: 480, height: 480, fit: "cover" }).webp({ quality: 78 }).toBuffer();
  const preview = await image.clone().rotate().resize({ width: 1600, withoutEnlargement: true }).webp({ quality: 84 }).toBuffer();
  return { thumbnail, preview, width: metadata.width, height: metadata.height };
}

rotate() handles EXIF orientation; withoutEnlargement avoids inflating small images.

Prepare uploads and track processing

Do not mark a file ready when the URL is issued. Create a pending record, then let a callback or worker move it to ready after verifying the object.

TS
import { Elysia, t } from "elysia";
import { createUploadUrl } from "../storage";
export const uploadRoutes = new Elysia({ prefix: "/uploads" }).post("/prepare", async ({ body, set }) => {
  const upload = await createUploadUrl(body.contentType);
  // Persist upload.key and userId with status=pending.
  set.status = 201;
  return { ...upload, status: "pending" };
}, { body: t.Object({ contentType: t.Union([t.Literal("image/jpeg"), t.Literal("image/png"), t.Literal("image/webp")]) }) });

The worker should verify object metadata again before calling Sharp.

Security and cost controls

Upload endpoints need limits at every stage.

Limit size

Limit API payloads and verify the actual object size after PUT.

Generate keys server-side

Use UUIDs and a user/project namespace instead of accepting arbitrary keys.

Scan or moderate

For public content, add antivirus or moderation before status becomes ready.

Add lifecycle rules

Automatically delete abandoned uploads and temporary originals.

Move heavy work to a worker

Keep the request API responsive by separating Sharp processing.

Test the image pipeline

Test decoded output, not just an HTTP status. This catches quality regressions and broken native dependencies after a Docker update.

TS
import { expect, test } from "bun:test";
import sharp from "sharp";
import { makeVariants } from "./images";
test("creates a bounded WebP thumbnail", async () => {
  const input = await sharp({ create: { width: 1200, height: 800, channels: 3, background: "#f97316" } }).png().toBuffer();
  const { thumbnail } = await makeVariants(input);
  const meta = await sharp(thumbnail).metadata();
  expect(meta.format).toBe("webp"); expect(meta.width).toBe(480); expect(meta.height).toBe(480);
});

Run bun test --coverage; use MinIO or a dedicated test bucket for storage integration tests.

FAQ

FAQ

Does Sharp work on Bun?

Yes, but Sharp has native dependencies. Install them for the target OS and architecture and run a production Docker smoke test.

Should files pass through Bun?

For large files, no. Issue a presigned URL and upload directly to S3-compatible storage; Bun should manage permissions, metadata, and processing state.

Reviewed: 27 Aug 2026Applies to: Bun 1.3+Applies to: AWS SDK v3Applies to: Sharp 0.34+Applies to: Amazon S3 or S3-compatible storageTested with: Bun runtimeTested with: @aws-sdk/client-s3Tested with: @aws-sdk/s3-request-presignerTested with: Sharp

Conclusion

Describe the task — first 15 minutes of consultation are free.

Related Articles

AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support
ai-assistants

AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support

A practical buyer guide to AI assistant development cost in 2026: prototypes, RAG chatbots, knowledge-base assistants, CRM and website integrations, guardrails, evaluations, monitoring, and support.

AI for landing page development: where it speeds up launches and where it hurts conversion
blogs

AI for landing page development: where it speeds up launches and where it hurts conversion

A practical research piece on using AI for landing page development: v0, Webflow AI, Builder.io, Framer-like builders, UX generation, copy, SEO, personalization, A/B testing, template risk, accessibility, security and technical debt.

AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents
growth

AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents

Search is shifting from clicks to answers. Bots and AI agents crawl, cite, recommend, and increasingly buy. Learn what AI SEO / GEO means, why classic SEO is no longer enough, and how PAS7 Studio helps brands win visibility in the agentic web.

The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records
blogs

The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records

A data-backed March 2026 analysis of Apple M5 Pro and M5 Max. We break down why these chips can credibly be called Apple's most powerful pro laptop silicon, how they compare with M4 Pro, M4 Max, M1 Pro, M1 Max, and how they stack up against Intel and AMD laptop rivals.

Professional development for your business

We create modern web solutions and bots for businesses. Learn how we can help you achieve your goals.