Storage

Every project gets S3-compatible object storage. You organize files into buckets, and each bucket can be private (access through your app / signed URLs) or public (files served directly over a permanent URL). Use it for avatars, uploads, exports, static assets, and more.

Concepts

TermMeaning
BucketA top-level container for files. Created public or private.
ObjectA single file, addressed by its path (e.g. user-123/avatar.png).
FolderA path prefix. There are no real directories — a / in the path groups files. avatars/2026/pic.png lives in the avatars/2026/ folder.
Public bucketObjects are readable by anyone at a permanent URL, no token. Good for assets.
Private bucketObjects require your app's session or a time-limited signed URL.

Buckets

Manage buckets with bf.storage.

import { createClient } from 'basefyio-js'
const bf = createClient()

// Create a bucket (private by default)
await bf.storage.createBucket('avatars')

// Create a PUBLIC bucket
await bf.storage.createBucket('assets', { public: true })

// List all buckets
const { data: buckets } = await bf.storage.listBuckets()
// [{ name, public, objectCount, totalSize, createdAt }]

// Change visibility (public ↔ private)
await bf.storage.updateBucket('avatars', { public: true })

// Delete a bucket (must be empty)
await bf.storage.deleteBucket('avatars')

Bucket names are 3–63 characters, lowercase letters, numbers and hyphens, and can't start or end with a hyphen.

Uploading files

Scope operations to a bucket with bf.storage.from(bucket). The path may include / to place the file in a folder — parent folders are created implicitly.

const bucket = bf.storage.from('avatars')

// Upload (browser File/Blob, Uint8Array or ArrayBuffer)
const { data, error } = await bucket.upload(
  'user-123/avatar.png',   // path — the "user-123/" folder is implied
  file,
  { contentType: 'image/png' }
)

// Uploading to the same path overwrites the existing object.
Max upload size is 50 MB per file via the API. For folders, see the dashboard's Upload folder action below.

Listing files & folders

// List the bucket root
const { data } = await bf.storage.from('avatars').list()

// List inside a folder (prefix)
const { data } = await bf.storage.from('avatars').list('user-123/')
// Entries with a "prefix" field are folders; entries with "name" are files:
// { name, size, lastModified, etag }  |  { prefix: 'user-123/' }

Downloading files

// Download as a Blob
const { data: blob } = await bf.storage.from('avatars').download('user-123/avatar.png')

// Delete one or more files
await bf.storage.from('avatars').remove([
  'user-123/avatar.png',
  'user-123/old.png',
])

Public links & signed URLs

Public bucket — objects are served directly, no token, at a permanent URL:

https://storage.basefyio.com/<internal-bucket>/<path>

From the dashboard (Storage tab) you can copy the public link for the whole bucket, any folder, or an individual file — and toggle a bucket public/private. For a private bucket, generate a time-limited signed URL instead:

// Temporary signed URL for a private object (default 1 hour, max 7 days)
const { data } = await bf.storage.from('reports').createSignedUrl(
  'q1/summary.pdf',
  { expiresIn: 3600 }   // seconds
)
// data.url  data.expiresIn
A folder/bucket public link is the base URL for its objects — appending a file path yields a working link. Object storage has no anonymous directory listing, so the base URL alone won't list contents.

In the dashboard

The Storage tab adds workflows beyond the SDK:

  • New folder — create an empty folder in the current location.
  • Upload folder — upload a whole local folder, preserving its structure; if files already exist you're asked to overwrite or skip them.
  • Public link — copy a shareable URL for a public bucket, folder, or file.
  • Toggle a bucket between public and private.

REST API

All routes are under https://api.basefyio.com/api/projects/:projectId/storage and require your API key / session.

MethodPathDescription
GET/bucketsList buckets
POST/bucketsCreate bucket { name, public }
PATCH/buckets/:bucketSet visibility { public }
DELETE/buckets/:bucketDelete bucket
GET/buckets/:bucket/public-url?path=Public URL (public buckets)
POST/buckets/:bucket/foldersCreate folder { path }
GET/buckets/:bucket/objects?prefix=List objects/folders
POST/buckets/:bucket/objects?path=Upload (multipart file)
POST/buckets/:bucket/objects/existsWhich of { paths } exist
GET/buckets/:bucket/objects/download?path=Download a file
GET/buckets/:bucket/objects/url?path=&expiry=Signed URL
DELETE/buckets/:bucket/objectsDelete { paths }