Skip to content
BunBase BunBase BunBase Docs Alpha v0.1.0

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

interface AuthUser {
id: string;
email: string;
created_at: number;
updated_at: number;
is_verified: boolean;
roles: string[];
metadata: Record<string, unknown>;
}

import { client } from "@/lib/bunbase";
// Full replace
await 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.",
});

The typical flow:

  1. Upload the image to storage → get back a FileRecord
  2. Build the download URL and write it into metadata
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),
});
}
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>
);
}

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>
);
}

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 record
await client.collection("profiles").create({
user_id: session.user.id,
display_name: "",
bio: "",
avatar_url: "",
});
// Read any user's public profile
const result = await client.collection("profiles").list({
filter: { user_id: targetUserId },
limit: 1,
});

See Public Author Profiles for the full guide on that pattern.


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,
});