bunderstackbunderstack/ docs

Storage

File uploads with local filesystem or S3

Upload

curl -X POST /api/files -F "[email protected]"
# 201 { fileId: "abc123.jpg", url: "/api/files/abc123.jpg" }

Uploads require an authenticated session by default. File ownership is tracked in an internal metadata table.

Retrieve / delete

curl /api/files/abc123.jpg
curl -X DELETE /api/files/abc123.jpg  # 204 — owner only by default

Access rules

bunderstack({
  schema,
  storage: {
    local: './uploads',
    defaultBucket: 'files',
    buckets: {
      files: {
        access: {
          create: 'authenticated', // default
          get: 'public', // default
          delete: 'owner', // default
        },
      },
    },
  },
})

Local storage

bunderstack({ schema, storage: { local: './uploads' } })

S3 / R2 / MinIO

bunderstack({ schema, storage: { s3: true } })
# Set S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY in .env
# For R2/MinIO also set S3_ENDPOINT
bunderstack({
  schema,
  storage: {
    local: './uploads',
    defaultBucket: 'files',
    buckets: {
      files: {
        upload: {
          accept: ['image/jpeg', 'image/png', 'image/webp'],
          maxSize: '5mb',
        },
      },
    },
  },
})

Programmatic URLs (app.storage.getUrl)

Use app.storage.getUrl to programmatically resolve presigned S3 download URLs in production or local proxy URLs in development:

// Programmatically get download URL for a file key
const downloadUrl = await app.storage.getUrl('resumes/user_123/cv.pdf', {
  expiresIn: 3600,
})

Server-side uploads (app.storage.upload)

Use app.storage.upload (or context.storage.upload in jobs and API procedures) to upload server-generated files such as PDFs and exports. It registers the storage metadata automatically, so the file is available through the generated download procedure and HTTP route:

await app.storage.upload(
  'adaptations/123/resume.pdf',
  pdfBytes,
  'application/pdf',
  { filename: 'resume.pdf', ownerId: user.id },
)

Nested keys (like adaptations/123/resume.pdf) are fully supported by GET /api/files/:bucket/* and DELETE /api/files/:bucket/*.

On this page