Work with storage objects
Upload files, download them, update their metadata, and delete them. The
examples use a bucket with slug BUCKET_SLUG and a file at
users/123/avatar.png.
Choose an interface below. REST examples use a server-side API key; keep it out of browser code.
Configure client with the JavaScript SDK setup or
the synchronous Python client setup. For
Refine, register the named storage provider
and initialize these hooks inside a React component or custom hook under
<Refine>. Browser examples use the signed-in user session; keep API keys in server code. Replace BUCKET_SLUG with your bucket slug.
Upload a file#
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageUploadBatchResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG').upload({
files: [file],
paths: ['users/123/avatar.png'],
metadatas: [{user_id: '123'}],
}).execute<StorageUploadBatchResponse>();
file is a browser File. The response’s data.successful lists uploaded
objects; check data.failed for per-file errors.
client.storage.from_("BUCKET_SLUG").upload(
files=[("avatar.png", file)],
paths=["users/123/avatar.png"],
metadatas=[{"user_id": "123"}],
)
file is an open binary stream for avatar.png. The returned dictionary’s
successful list contains uploaded objects; check failed for per-file errors.
import {useCreate} from '@refinedev/core';
const {mutate: upload} = useCreate();
// Call from an event handler.
upload({
dataProviderName: 'storage',
resource: 'BUCKET_SLUG',
values: {
files: [file],
paths: ['users/123/avatar.png'],
metadatas: [{user_id: '123'}],
},
});
file is a browser File. Send one file with useCreate. Its onSuccess
callback receives the uploaded object in data, or per-file errors in
data.failed; check for those errors before treating the upload as successful.
See batch uploads
for several files.
Upload to a path in the URL with PUT:
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Multipart form
fileFile: avatar.pngmetadata- {"user_id": "123"}
View cURL
curl --silent --show-error -X PUT "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--form-string 'metadata={"user_id":"123"}'
201201 when a new object is created; 200 when an existing object is replaced.
Or POST to the collection with the path as a form field:
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Multipart form
fileFile: avatar.pngpathusers/123/avatar.pngmetadata- {"user_id": "123"}
View cURL
curl --silent --show-error -X POST "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--form-string 'path=users/123/avatar.png' \
--form-string 'metadata={"user_id":"123"}'
201201 when a new object is created; 200 when an existing object is replaced.
To send raw bytes instead of a form, use PUT with the path in the URL and
pass metadata as X-Metadata-* headers:
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/Headers
AuthorizationApi-Key $TARUVI_API_KEYContent-Typeimage/pngX-Metadata-User-Id123
Request body
Fileavatar.png
View cURL
curl --silent --show-error -X PUT "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: image/png" \
-H "X-Metadata-User-Id: 123" \
--data-binary '@avatar.png'
201201 when a new object is created; 200 when an existing object is replaced.
The SDK upload methods call POST …/objects/batch-upload/. The call succeeds
with 200 when every file uploads and 207 when some don't, and a rejected file
doesn't raise: check the response’s data.failed in JavaScript or the returned
dictionary’s failed list in Python for each file's error.
The single-file REST upload returns 201 Created; uploading to an existing path
replaces the file. The response includes the file's file_path, size, mimetype,
metadata, and effective visibility. A file is rejected, with 400 from the
single-file upload or an entry in failed from a batch, when:
- the file is empty;
- the file is larger than the bucket's file size limit;
- the bucket restricts file types and this type isn't allowed (wildcards such as
image/*are supported); - the metadata is larger than 2 KB.
Download a file#
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.download('users/123/avatar.png').execute<Blob>();
The result is the file's content as a Blob.
client.storage.from_("BUCKET_SLUG").download("users/123/avatar.png")
download returns bytes and raises NotFoundError or AuthorizationError
when the request fails.
import {useOne} from '@refinedev/core';
useOne<Blob>({
dataProviderName: 'storage',
resource: 'BUCKET_SLUG',
id: 'users/123/avatar.png',
});
id is the object path. The hook’s result contains the downloaded Blob
after the request succeeds. Use meta: {metadata: true} with useOne<StorageObject>
to request the object's details.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Save response
Fileavatar.out.png
View cURL
curl --silent --show-error "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--output 'avatar.out.png'
200
Add ?metadata=true to get the file's details as JSON instead of its content.
Anyone can download a public file without a credential. A private file needs a
caller whom the bucket's policy allows to read it; otherwise the request returns
403. A 404 means no file exists at that path — paths are case-sensitive.
Update metadata#
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.update('users/123/avatar.png', {metadata: {status: 'verified'}})
.execute<StorageResponse>();
The response’s data contains the updated object.
client.storage.from_("BUCKET_SLUG").update(
"users/123/avatar.png", metadata={"status": "verified"},
)
The method returns the updated object.
import {useUpdate} from '@refinedev/core';
const {mutate: update} = useUpdate();
// Call from an event handler.
update({
dataProviderName: 'storage',
resource: 'BUCKET_SLUG',
id: 'users/123/avatar.png',
values: {metadata: {status: 'verified'}},
});
id is the object path; values.metadata contains the updated metadata.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/Headers
AuthorizationApi-Key $TARUVI_API_KEYContent-Typeapplication/json
Request body
{
"metadata": {
"status": "verified"
}
}
View cURL
curl --silent --show-error -X PATCH "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"metadata": {
"status": "verified"
}
}
JSON
200
PATCH changes details only. To replace the file itself, upload to the same
path again. A file's visibility always follows its bucket; to change it, update
the bucket's visibility.
Delete a file#
Deleting removes the file from TaruviBase and from the storage provider. There is no
recycle bin. The caller needs a policy rule that allows delete — neither default
bucket policy includes one. The SDK delete methods call
POST …/objects/batch-delete/ and don't raise when a file is missing or the
policy denies the delete; those paths come back in failed. Check that
deleted_count equals the number of paths you sent. See
Security and limits.
- JavaScript SDK
- Python SDK
- Refine
- REST API
import {Storage} from '@taruvi/sdk';
import type {StorageDeleteBatchResponse} from '@taruvi/sdk';
await new Storage(client).from('BUCKET_SLUG')
.delete(['users/123/avatar.png']).execute<StorageDeleteBatchResponse>();
client.storage.from_("BUCKET_SLUG").delete(["users/123/avatar.png"])
import {useDelete} from '@refinedev/core';
const {mutate: remove} = useDelete();
// Call from an event handler.
remove({
dataProviderName: 'storage',
resource: 'BUCKET_SLUG',
id: 'users/123/avatar.png',
});
Confirm the target path before calling remove. id is the object path.
Refine reports skipped deletes through the
mutation's error state: NotFoundError for a missing file and
ForbiddenError for policy denial.
/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/Headers
AuthorizationApi-Key $TARUVI_API_KEY
View cURL
curl --silent --show-error -X DELETE "$TARUVI_SITE_URL/api/apps/APP_SLUG/storage/buckets/BUCKET_SLUG/objects/users/123/avatar.png/" \
-H "Authorization: Api-Key $TARUVI_API_KEY"
200Returns success after deletion; this operation is irreversible.
Related pages#
- Organize files — list, browse, search, copy, and move
- Batch and SharePoint — batch operations and Office editing
- REST reference — every endpoint
- Filter reference — list query parameters