Server SDK

Mint presigned URLs, manage files, abort multiparts. Node, Bun, edge runtimes.
The API key must never be exposed to the browser. Always import from sonuslab-storage/server in a server-only file (Nitro server/api, Node script, edge function).

Constructor

import { StorageClient } from 'sonuslab-storage/server'

const storage = new StorageClient({
  apiKey: process.env.SONUSLAB_STORAGE_API_KEY!,
})
NameTypeRequiredDescription
apiKeystringyessl_... token from the app Keys tab.
baseUrlstringnoDefaults to https://storage-api.sonuslab.dev. Override only when self-hosting.
fetchtypeof fetchnoOverride the fetch implementation (Cloudflare Workers, tests, etc).

Self-hosting

Running your own storage-api? Pass baseUrl: 'https://storage-api.yourdomain.com' to point the SDK at your deployment.

storage.upload(input)

Upload bytes directly from your server. Best for small files (under the single PUT limit).

const file = await storage.upload({
  name: 'report.pdf',
  contentType: 'application/pdf',
  data: pdfBuffer,
  metadata: { userId: 'abc' },
})
NameTypeRequiredDescription
namestringyesOriginal file name (visible in the dashboard).
contentTypestringyesMIME type — must match the actual bytes.
dataBuffer | Uint8Array | BlobyesThe file contents.
metadataRecord<string, string>noArbitrary key/value pairs stored alongside the file.

storage.presign(input)

Mint a short-lived upload URL for the browser.

const presign = await storage.presign({
  name: 'avatar.png',
  size: file.size,
  contentType: 'image/png',
})

// presign.uploadUrl  → short-lived upload URL (browser PUTs to it)
// presign.fileId     → opaque id used for completeUpload()
// presign.key        → final object key
NameTypeRequiredDescription
namestringyesOriginal file name.
sizenumberyesByte size of the file. Validated against the app maxFileSize.
contentTypestringyesMIME type.
metadataRecord<string, string>noStored on the file once the upload finalizes.

storage.completeUpload({ fileId })

Finalize a presigned upload after the browser PUT succeeds.

const file = await storage.completeUpload({ fileId: presign.fileId })
NameTypeRequiredDescription
fileIdstringyesThe id returned by presign().

storage.initMultipart(input)

Start a multipart upload session (server-side, for large files).

const init = await storage.initMultipart({
  name: 'movie.mp4',
  size: file.size,
  contentType: 'video/mp4',
})

// init.uploadId, init.key, init.fileId
NameTypeRequiredDescription
namestringyesOriginal file name.
sizenumberyesTotal file size in bytes.
contentTypestringyesMIME type.
metadataRecord<string, string>noStored on the file once completed.

storage.presignMultipartPart(uploadId, key, partNumber)

Mint an upload URL for one part of a multipart upload.

const { uploadUrl, partNumber } =
  await storage.presignMultipartPart(init.uploadId, init.key, 1)

storage.completeMultipart(input)

Finalize a multipart upload after all parts succeed.

const file = await storage.completeMultipart({
  uploadId: init.uploadId,
  key: init.key,
  fileId: init.fileId,
  parts: [{ partNumber: 1, etag: '...' }, ...],
})
NameTypeRequiredDescription
uploadIdstringyesFrom initMultipart.
keystringyesFrom initMultipart.
fileIdstringyesFrom initMultipart.
parts{ partNumber, etag }[]yesOrdered list of completed parts.

storage.abortMultipart(uploadId, key, fileId)

Abort a multipart upload (call this if the user cancels).

await storage.abortMultipart(init.uploadId, init.key, init.fileId)

Paginated list of files in the app.

const { items, nextCursor } = await storage.listFiles({
  limit: 50,
  search: 'avatar',
})

while (cursor) {
  const page = await storage.listFiles({ cursor })
  // ...
}
NameTypeDescription
limitnumberPage size. Default 50, max 200.
cursorstringOpaque pagination token from a previous call.
searchstringSubstring match against file name.

storage.getFile(id)

Fetch a single file by id.

const file = await storage.getFile('file_abc')

storage.getDownloadUrl(id, opts?)

Resolve a readable URL for a file. Returns the stable CDN URL on public apps, a short-lived presigned GET on private ones.

const url = await storage.getDownloadUrl('file_abc', { expiresIn: 600 })
NameTypeDescription
expiresInnumberLifetime in seconds, 1–604800. Default 300. Ignored on public apps — the CDN URL never expires.

storage.download(id)

Fetch the stored bytes plus the file record.

const { data, file } = await storage.download('file_abc')
// data : Uint8Array — ciphertext if the file was uploaded encrypted
// file : StorageFile

storage.deleteFile(id)

Soft-delete a file. Recoverable through the grace window, then permanently removed.

await storage.deleteFile('file_abc')
Copyright © 2026