Links

Handling Attachments

Examples of syncing attachments between a client app and Supabase Storage.

React Native Example

Our React Native To-Do List example app showcases how to sync attachments (such as photos) using the @journeyapps/powersync-attachments library, the PowerSync Service, and Supabase.
In this example, we are syncing photos, however other media types, such as PDFs, are also supported.
The library and this example implementation can be used as a reference for implementing similar functionality for a Postgres backend without Supabase.
The below assumes you have completed the steps outlined in the To-Do List app Readme. This includes installing and running the PowerSync React Native SDK; setting up a Supabase project; setting up a PowerSync instance and connecting it with Supabase.

Configure Storage in Supabase

In this example app, Supabase Storage is used to store and serve attachments. To configure this for your app, navigate to the Storage section of your Supabase project and create a new bucket:
Give the storage bucket a name, such as media, and hit Save:
Next, link this storage bucket to your app by opening up the AppConfig.ts file and adding the bucket name as the value to the supabaseBucket key:
Lastly, configure a policy for this bucket. In this example app, we will allow all user operations on the media bucket.
Create a new policy for the media bucket:
Give the new policy a name, and allow SELECT, INSERT, UPDATE, and DELETE.
Proceed to review and save the policy.
This concludes the necessary configuration for handling attachments in the To-Do List example app. When running the app now, a photo can be taken for a to-do list item, and PowerSync will ensure that the photo syncs to Supabase and other devices (if sync rules allow).
Read on to learn more about how this works under the hood.

Implementation Details

The @journeyapps/powersync-attachments library is used in conjunction with the PowerSync Service to sync photos. Refer to the library's README for an overview of the main components. In summary, they are:
  • AttachmentRecord to store the metadata of attachments.
  • AttachmentState to track the sync state of an AttachmentRecord.
  • AbstractAttachmentQueue class to manage and sync AttachmentRecords:
    • Track and sync attachment metadata.
    • Watch for changes and handle CRUD operations on AttachmentRecords.
    • Store attachment data on the user's local storage, using file URIs on the device.
The UI of the example app supports taking photos as follows:
  • CameraWidget uses expo-camera to allow users to capture a photo.
  • The photo is stored on the user's local storage.
  • The app includes a basic prompt for the user to grant permission to use the device's camera.
The app's schema was modified to link a photo to a to-do item:
  • A photo_id was added as a column to the todos table to link a photo to a to-do item.
  • A local-only attachments table is instantiated to store the metadata of photos.
The PhotoAttachmentQueue class extends the AbstractAttachmentQueue abstract class and:
  • Uses a PowerSync query to gather the relevant photo IDs (see attachmentIds())
  • Creates AttachmentRecords to store photo metadata. (see newAttachmentRecord())
  • Uses the savePhoto() method to save photos into local storage and add them to the sync queue.

How Syncing Works

Refer to this section in the library's README to learn more about the various sync states and operations.

Future Improvements

The following improvements can be considered for this implementation.
  • An easier way to set up the local-only attachments table and related schema.
  • Better tooling/APIs for retrying/resuming uploads or downloads when transitioning from an offline into an online state.

Flutter Example

Our Flutter To-Do List example app showcases how to sync attachments (such as photos) using our powersync_attachments_helper package for Flutter.

See Also