Skip to main content
All file uploads in RefactKit go through src/server/storage-fns.ts — a server function that runs exclusively on Nitro. This design keeps SUPABASE_SERVICE_ROLE_KEY off the client entirely: the browser never holds credentials, and Supabase Row Level Security policies are never bypassed from untrusted code.

How the upload workflow works

The client collects a file, wraps it in FormData, and calls the uploadImage server function. The server validates the file, uploads it to Supabase Storage using the service role key, and returns the public URL. The client stores that URL in local state and uses it for an instant preview.

Set up the avatars bucket

Before your first upload, create the avatars bucket in Supabase. Run this in your Supabase Dashboard → SQL Editor:
For a gallery or any other bucket, replace 'avatars' with your bucket name and adjust the policy as needed.

The uploadImage server function

src/server/storage-fns.ts is the single upload entry point. It accepts a FormData body with two fields — file and bucket:

Call the upload function from a component

Pass a FormData object directly — do not JSON-encode binary data.

Show an instant preview with derived state

After a successful upload, you want to display the new image immediately — before the user saves the form — while still falling back to the existing value from the database. Use a single derived constant rather than two separate state variables:
This pattern avoids flicker: currentImg updates the moment setUploadedImg is called, without waiting for a round-trip to the database.

Validation rules

The built-in uploadImage function enforces the following: To add content-type validation, insert a check before the upload call:
SUPABASE_SERVICE_ROLE_KEY bypasses Supabase Row Level Security entirely. Never prefix it with VITE_ and never reference it from any file that is part of the client bundle. It must only be used inside server functions in src/server/ or server-only library files like lib/supabase.ts.