User Profiles (Avatar, Bio & Metadata)
Every BunBase user has a metadata field — a freeform JSON object you can use to store profile data: display name, bio, avatar URL, social links, preferences, or anything else your app needs.
metadata is:
- Readable by the owner via
GET /api/v1/auth/me - Writable by the owner via
PATCH /api/v1/auth/me - Readable and writable by admins via
PATCH /api/v1/admin/users/:id - Not readable or writable by other users
User profile shape
Section titled “User profile shape”interface AuthUser { id: string; email: string; created_at: number; updated_at: number; is_verified: boolean; roles: string[]; metadata: Record<string, unknown>;}Updating metadata
Section titled “Updating metadata”import { client } from "@/lib/bunbase";
// Full replaceawait client.auth.updateMe({ display_name: "Jane Doe", bio: "Building things with BunBase.", website: "https://janedoe.dev",});updateMe does a full replace of the metadata object. Merge with the current value if you only want to change one field:
const me = await client.auth.me();
await client.auth.updateMe({ ...me.metadata, bio: "Updated bio.",});Uploading an avatar
Section titled “Uploading an avatar”The typical flow:
- Upload the image to storage → get back a
FileRecord - Build the download URL and write it into
metadata
With the TypeScript SDK
Section titled “With the TypeScript SDK”async function uploadAvatar(file: File) { const fileRecord = await client.storage.upload(file, { bucket: "avatars", isPublic: true, });
const me = await client.auth.me(); await client.auth.updateMe({ ...me.metadata, avatar_url: client.storage.downloadUrl(fileRecord.id, fileRecord.filename ?? undefined), });}With the React SDK
Section titled “With the React SDK”import { useAuth, useUpload, useBunBase } from "@bunbase/react-sdk";
function AvatarUploader() { const { user } = useAuth(); const { upload, loading, progress } = useUpload(); const { client } = useBunBase();
async function handleChange(e: React.ChangeEvent<HTMLInputElement>) { const file = e.target.files?.[0]; if (!file) return;
const record = await upload(file, { bucket: "avatars", isPublic: true });
await client.auth.updateMe({ ...user?.metadata, avatar_url: client.storage.downloadUrl(record.id, record.filename ?? undefined), }); }
return ( <div> {user?.metadata?.avatar_url && ( <img src={user.metadata.avatar_url as string} alt="avatar" width={80} height={80} style={{ borderRadius: "50%" }} /> )} <label> <input type="file" accept="image/*" hidden onChange={handleChange} /> <span>{loading ? `${Math.round(progress * 100)}%` : "Change avatar"}</span> </label> </div> );}Full profile form (React SDK)
Section titled “Full profile form (React SDK)”import { useAuth, useBunBase } from "@bunbase/react-sdk";import { useState } from "react";
function ProfileForm() { const { user } = useAuth(); const { client } = useBunBase(); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); const form = new FormData(e.currentTarget); setSaving(true); setSaved(false); try { await client.auth.updateMe({ ...user?.metadata, display_name: form.get("display_name") as string, bio: form.get("bio") as string, website: form.get("website") as string, }); setSaved(true); } finally { setSaving(false); } }
const meta = user?.metadata ?? {};
return ( <form onSubmit={handleSubmit}> <label> Display name <input name="display_name" defaultValue={(meta.display_name as string) ?? ""} /> </label> <label> Bio <textarea name="bio" defaultValue={(meta.bio as string) ?? ""} rows={4} /> </label> <label> Website <input name="website" type="url" defaultValue={(meta.website as string) ?? ""} /> </label> <button disabled={saving}>{saving ? "Saving…" : "Save profile"}</button> {saved && <p>Profile saved.</p>} </form> );}Reading another user’s profile
Section titled “Reading another user’s profile”metadata is private — only the owner and admins can read it. If your app needs public profiles (e.g. showing author names on posts), use a separate profiles collection:
// On register — create a public profile recordawait client.collection("profiles").create({ user_id: session.user.id, display_name: "", bio: "", avatar_url: "",});
// Read any user's public profileconst result = await client.collection("profiles").list({ filter: { user_id: targetUserId }, limit: 1,});See Public Author Profiles for the full guide on that pattern.
Admin: update user metadata
Section titled “Admin: update user metadata”Admins can update any user’s metadata (and roles, email, verification status) via:
await client.admin.updateUser(userId, { metadata: { display_name: "Jane Doe", avatar_url: "..." }, roles: ["moderator"], is_verified: true,});