S
Supabase•2mo ago
Jacob

Upload file to bucket with metadata from client side

Hey everyone! I'm using the JavaScript library with Supabase client to upload/list/download files from client side. I would like to store custom metadata like the real filename. Currently I upload like:
const { data, error } = await supabase.storage
.from('bucket')
.upload('path/file.txt', file, {
metadata: { filename: 'My Real Filename.txt' }
});
const { data, error } = await supabase.storage
.from('bucket')
.upload('path/file.txt', file, {
metadata: { filename: 'My Real Filename.txt' }
});
But when I list files, the metadata only shows system fields (eTag, size, mimetype), not my custom filename:
const { data } = await supabase.storage.from('bucket').list('folder');
console.log(data[0].metadata); // Only system metadata, no custom fields
const { data } = await supabase.storage.from('bucket').list('folder');
console.log(data[0].metadata); // Only system metadata, no custom fields
How do I retrieve the custom metadata I uploaded? Is there a getMetadata() method or different approach needed?
15 Replies
Jacob
JacobOP•2mo ago
Update: found something related The .info() method works for retrieving custom metadata:
const info = await supabase.storage.from('bucket').info('path/file.txt');
console.log(info.data.metadata); // Shows custom fields!
const info = await supabase.storage.from('bucket').info('path/file.txt');
console.log(info.data.metadata); // Shows custom fields!
But you need N queries for N files (vs 1 query with .list()), so there's a performance trade-off. Using filename patterns for now but .info() definitely works if needed.
garyaustin
garyaustin•2mo ago
I had just found that as I could not find it in docs. That is the only way I know currently. But it makes sense for list to do it. I would file an issue in supabase/storage asking for that to be in the list.
Jacob
JacobOP•2mo ago
Hmm I'll create new issue about it. looks like the server should return it @graboosky 🥷 I'm thinking about a workaround to make a server-side endpoint that receives list of IDs and returns the user metadata
garyaustin
garyaustin•2mo ago
If you check in the body of the response is it there?
Jacob
JacobOP•2mo ago
since the paths are UUIDs it should be relatively safe. what do you think? It's not there sadly with list() Only with info() for individual file
garyaustin
garyaustin•2mo ago
So you looked in the network data coming back and not there. So not a storage-js issue, but a storage server issue. You can have an rpc access storage.objects and return what ever you like.
Jacob
JacobOP•2mo ago
Sounds good. like RPC that will return the metadata list for list of object IDs?
garyaustin
garyaustin•2mo ago
You can use whatever filter you want on storage.objects and return what you feel is safe. You can use auth.uid() to limit to that user.
Jacob
JacobOP•2mo ago
Yea it doesn't return the metadata. it's null. but it's there in the table in the storage schema!
garyaustin
garyaustin•2mo ago
Yes info works so it is stored correctly. Just list does not seem to have it based on what you are saying. I've not test list. I'll do that. I tested info and it is working.
Jacob
JacobOP•2mo ago
btw I don't want to invent new list function and stick to supabase because of the pagination and security they put into it yea info works for me too
garyaustin
garyaustin•2mo ago
List does not return it. Just verified.
Jacob
JacobOP•2mo ago
Hmm sounds like the only option is to create custom list RPC function that returns metadata+user metadata I see owner_id in the objects schema, will try to verify if it's equal to the sub (user id)
garyaustin
garyaustin•2mo ago
For the moment. I would ask them to add it. Seems like an oversite.
Jacob
JacobOP•2mo ago
the owner id equals to the sub!! cool will try now to use something like this
-- Function to list files for the current authenticated user
create or replace function list_user_files()
returns table (
id uuid,
bucket_id text,
name text,
created_at timestamptz,
updated_at timestamptz,
metadata jsonb,
user_metadata jsonb
)
language sql
stable
security definer
as $$
select
o.id,
o.bucket_id,
o.name,
o.created_at,
o.updated_at,
o.metadata,
o.user_metadata
from storage.objects o
where o.owner_id = get_user_storage_uuid();
$$;

-- Grant execute permission to authenticated users
GRANT EXECUTE ON FUNCTION list_user_files() TO authenticated;

-- 3. Expose it via RLS policies (so each user only sees their own rows)
-- Make sure RLS is enabled on storage.objects if not already:
-- alter table storage.objects enable row level security;

create policy "Users can select their own files"
on storage.objects
for select
to authenticated
using (owner_id = get_user_storage_uuid());

-- Add comment for documentation
COMMENT ON FUNCTION list_user_files() IS 'Returns all files owned by the current authenticated user from storage.objects';
-- Function to list files for the current authenticated user
create or replace function list_user_files()
returns table (
id uuid,
bucket_id text,
name text,
created_at timestamptz,
updated_at timestamptz,
metadata jsonb,
user_metadata jsonb
)
language sql
stable
security definer
as $$
select
o.id,
o.bucket_id,
o.name,
o.created_at,
o.updated_at,
o.metadata,
o.user_metadata
from storage.objects o
where o.owner_id = get_user_storage_uuid();
$$;

-- Grant execute permission to authenticated users
GRANT EXECUTE ON FUNCTION list_user_files() TO authenticated;

-- 3. Expose it via RLS policies (so each user only sees their own rows)
-- Make sure RLS is enabled on storage.objects if not already:
-- alter table storage.objects enable row level security;

create policy "Users can select their own files"
on storage.objects
for select
to authenticated
using (owner_id = get_user_storage_uuid());

-- Add comment for documentation
COMMENT ON FUNCTION list_user_files() IS 'Returns all files owned by the current authenticated user from storage.objects';
Created new issue in supabase storage https://github.com/supabase/storage/issues/759

Did you find this page helpful?