P
Persist

S3 Adapter

Store blobs in S3-compatible object storage with multipart uploads, presigned URLs, and R2 support.

Overview

The S3 adapter handles binary object storage. It supports AWS S3, Cloudflare R2, MinIO, and any S3-compatible service. Use it for files, images, backups, and other large binary data that does not fit well in SQLite or Postgres.

Bucket configuration

import { createStore } from "@cuitty/persist";

const store = await createStore("assets", {
  adapter: "s3",
  s3: {
    bucket: "my-app-assets",
    region: "us-east-1",
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    },
  },
});

For Cloudflare R2, set the endpoint to your R2 URL:

const store = await createStore("assets", {
  adapter: "s3",
  s3: {
    bucket: "my-app-assets",
    region: "auto",
    endpoint: "https://<account-id>.r2.cloudflarestorage.com",
    credentials: {
      accessKeyId: process.env.R2_ACCESS_KEY_ID,
      secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
    },
  },
});

Multipart uploads

Files larger than 5 MB are automatically uploaded using S3 multipart upload. Persist splits the file into 8 MB parts and uploads them in parallel. If an upload is interrupted, partial parts are cleaned up automatically.

// Large file upload — multipart happens transparently
const buffer = await fs.readFile("./backup.tar.gz"); // 500 MB
await store.blobs.put("backups/2026-05-11.tar.gz", buffer);

You can configure the part size and concurrency:

const store = await createStore("assets", {
  adapter: "s3",
  s3: {
    bucket: "my-app-assets",
    multipart: {
      partSize: 16 * 1024 * 1024, // 16 MB parts
      concurrency: 4,
    },
  },
});

Presigned URLs

Generate temporary URLs that grant time-limited access to a blob without exposing credentials.

const url = await store.blobs.presign("reports/q1.pdf", {
  expiresIn: 3600, // 1 hour
  method: "GET",
});
// Share `url` with a client — no credentials needed

Supported store classes

The S3 adapter supports blobs only. For records, events, and kv, pair it with the SQLite or Postgres adapter. Persist supports using multiple adapters in a single application, each backing a different store.