How To Upload Files To Supabase Storage Buckets and Write Data To Supabase Using Remix

Overview

Simple application showing file upload and writing database records using Remix and Supabase

We show how Actions and Loaders work to manager working with server for data and to make API calls. In both of the examples presented in video, the actions are processing the form data and then making the appropriate calls to Supabase. We then take the responses we get from Supabase and populate action data which is returned to the page and rendered appropriately.

Remix is a full stack web framework that lets you focus on the user interface and work back through web fundamentals to deliver a fast, slick, and resilient user experience.

The Video

Writing Data

This is the action function related to writing new record to the database. The form that is being processed has an input elements for all of the fields to be written to database. On successful write to database, we redirect back to the default route of application

export const action = async ({ request }) => {
  // get data from form
  let form = await request.formData();
  let name = form.get("name");
  let description = form.get("description");
  let state = form.get("state");

  // use form information to write to supabase
  const { data, error } = await supabaseClient
    .from("chargers")
    .insert([{ name, description, state }]);

  // if no error, back to home page... index.jsx
  if (!error) {
    return redirect("/", {});
  }

  // else stay on page and return error information
  return { data, error };
};

Uploading Files

Currently a bug in Remix when uploading large files. In this example I am only using small files to show how the process works

This example is derived from the Remix documentation on uploadHandler which can be found here

Access To Storage Buckets

This is the SQL script I used to allow you to upload files to Supabase Storage. I am not restricting access for the purpose of this demo but you can read up more or on creating policy here in the Supabase Documentation

replace my bucket id "images" with the name of your bucket

create policy "ALL images are publicly accessible."
  on storage.objects for select
  using ( bucket_id = 'images' );

create policy "Anyone can upload an image."
  on storage.objects for insert
  with check ( bucket_id = 'images' );

create policy "Anyone can update an image."
  on storage.objects for update
  with check ( bucket_id = 'images' );

This is the action function related to file upload. The form that is being processed has an input element with the id my-file

export const action = async ({ request }) => {
  try {
    /**
     *
     * @param {*} param0
     * @returns
     */
    let uploadHandler = async ({ name, stream, filename }) => {
      console.log("in uploadHandler");

      if (name !== "my-file") {
        stream.resume();
        return;
      } else {
        console.log(name, filename);
      }

      // Get the file as a buffer
      const chunks = [];
      for await (const chunk of stream) chunks.push(chunk);
      const buffer = Buffer.concat(chunks);

      // call supabase function for uploading to bucket
      const { data, error } = await supabaseClient.storage
        .from("images")
        .upload(filename, buffer);
      if (error) {
        throw error;
      }

      // return information up uploaded file
      return JSON.stringify({ data });
    };

    // get file info back after image upload
    const form = await unstable_parseMultipartFormData(request, uploadHandler);

    //convert it to an object to padd back as actionData
    const fileInfo = JSON.parse(form.get("my-file"));

    // this is response from upload handler
    console.log("the form", form.get("my-file"));

    // return success action data
    return fileInfo;
  } catch (e) {
    // return error action data
    return { error: e };
  }
};

21