🆓Self-hosting AppFlowy for free Using Supabase
Last updated
Last updated
AppFlowy is a privacy-first, open source workspace for your notes, wikis, projects, and more. You are in charge of your data and customizations, with no vendor lock-in.
Over the past year, we've been working hard alongside thousands of community members towards our mission: to empower everyone to create workspaces that suit their needs, without limits on what's possible. We have been determined to uphold our three core values from the very beginning:
Data privacy first
Reliable native experience
Community-driven extensibility
Today, we're pleased to share that AppFlowy can now be self-hosted. This article will delve into what's behind the scenes and guide you on how to self-host AppFlowy using Supabase.
If you prefer a video tutorial click here:
In order to implement cloud features such as user sign-ins and data storage, we've partnered with Supabase, an open-source alternative to Firebase.
With a Supabase project, transforming AppFlowy into a self-hosted cloud app is straightforward. By the end of this article, you'll be primed to launch your very own AppFlowy cloud application with ease. Furthermore these steps are similar to other cloud services, such as Appwrite
or Firebase
, should you want to use those services for an AppFlowy cloud implementation.
Let's explore how AppFlowy uses Supabase to implement the cloud features.
Initially, we considered building our own authentication service from scratch. The feature set we were looking to support included social third-party authentication and magic links as well as traditional email & password login.
However, we soon realized that there were open-source projects available that already offer these features. That led us to the discovery of the Supabase authentication service, which provides multiple methods for user authentication.
Email & password
Magic links
Social providers
Phone logins
This is exactly what we need.
We can leverage Supabase's authentication service to implement AppFlowy's authentication system. After following the instructions for the Supabase auth documentation, we successfully set up the authentication service.
Let's explore how we integrated Supabase authentication into AppFlowy.
Integrating AppFlowy with Supabae requires the supabase_flutter Flutter package.
We utilize the env
file to inject the supabase configuration into the app. Simply adding supabase_flutter
and envied
to the pubspec.yaml
is all that's needed to kickstart the process.
Follow the instructions here to setup the .env
file.
Then, create the env.dart
file, which defines the global environment variables.
The .env.dart
file will look as follows:
Afterwards, run the following command to generate the env.g.dart
file. This file contains the environment variables we defined in the .env
file. These variables are encrypted to prevent them from being exposed in the source code.
Before utilizing the Supabase instance, it must be initialized. This initialization takes place in the supabase_task.dart
file. We define the SupabaseLocalStorage
to customize the storage of the Supabase auth data.
Our authentication logic is provided in an abstract class named AuthService
.
This class's Supabase implementation can be found in SupabaseAuthService
, specifically within the supabase_auth_service.dart
file.
The steps for the authentication flow are outlined below:
The user clicks the login button in AppFlowy.
AppFlowy requests Supabase to start the authentication process.
Supabase prompts a web browser to open for authentication.
The user enters their credentials in the browser.
Once entered, the credentials are sent to Supabase for verification.
Supabase verifies the credentials and sends a user token back to AppFlowy.
AppFlowy updates to show the user they're logged in.
By clicking a login button for Google, users are directed to a web browser to finalize the authentication process. Once authenticated, they are redirected back to AppFlowy.
We will support other social logins later, including GitHub, Discord, Slack, and more. The process for each is quite similar
One of the prerequisites for AppFlowy Data Storage is support for multiple cloud services.
To achieve this, we've designed an abstraction layer that facilitates the integration of various cloud services. Each implementation provides the specific logic for the functions outlined in the AppFlowyServer interface.
The architectural design is illustrated below:
Using the AppFlowyServer
interface we can easily transition between various cloud services. For instance, moving from Supabase to Firebase or Appwrite is merely a matter of changing the implementation of the AppFlowyServer
interface.
The AppFlowyServer
encompasses five services, with each service catering to a unique set of functionalities. Additionally, each service defines its own interface. Let's explore each of these components in detail.
The UserCloudService represents a standardized set of functionalities for interacting with a cloud-based user management service.
This includes operations for:
registering new users
authenticating existing users
managing user profiles
handling user workspaces
In addition to basic user management, the interface provides methods for managing collaborative objects, subscribing to user updates, and receiving real-time events.
The DocumentCloudService
represents a set of standardized functionalities for managing and retrieving information related to documents in a cloud service.
It facilitates fetching updates to a given document, obtaining snapshots of a document up to a specified limit, and accessing the primary data of a document.
This interface is designed to offer asynchronous operations to ensure efficient and non-blocking interactions when dealing with document data in a cloud environment.
The DatabaseCloudService
represents a set of standardized operations focused on collaborative objects within a AppFlowy
database.
This interface allows for the retrieval of updates for a specific collaborative object, batch fetching of updates for multiple collaborative objects of a given type, and obtaining a set number of snapshots for a collaborative object.
These functionalities ensure efficient and streamlined interactions when managing and accessing collaborative data in a cloud database environment.
The RemoteCollabStorage
interface provides a suite of operations for managing collaborative objects in a remote storage system. This includes checking whether the remote storage is active and functions for retrieving all updates, snapshots, and the state of a collaborative object.
Furthermore, there are methods to create snapshots and dispatch updates or initial states to the remote storage.
The service is built with asynchronous operations in mind, as evident by the async
keyword, ensuring that activities like fetching or sending data don't block other operations. It also allows subscribing to remote updates, giving users the capability to stay updated with changes in the collaborative object.
The FolderCloudService
interface outlines a set of operations centered around managing workspaces and folders in a cloud environment. It offers capabilities to create a new workspace and fetch data, snapshots, and updates related to a specific folder within a workspace.
Currently, we only support Supabase as a cloud service provider. However, we intend to support other cloud services in the future.
Let's explore how to implement the AppFlowyServer
using Supabase.
Upon integrating Supabase authentication, we discovered it inherently offers a PostgreSQL database. This advantage implies that individual users self-hosting AppFlowy on Supabase won't require separate data storage setups.
Each Supabase project is pre-equipped with a dedicated PostgreSQL database, which we leverage to store AppFlowy's data.
We utilize the Postgres database to store:
User profiles
User settings
Data related to documents and databases generated within AppFlowy
Relationships between users and their associated workspaces
Here's what our schema looks like:
Let's introduce some of the tables and views in the schema:
af_roles
table: Captures roles, e.g., 'Owner', 'Member', 'Guest'.
af_permissions
table: Chronicles permissions, each featuring a name, description, and access tier.
af_role_permissions
table: A junction table representing permissions assigned to roles.
af_user
table: Houses user details, including a unique identifier (uid).
af_workspace
table: Lists workspaces, each linked to a user via the user's uid.
af_workspace_member
table: Associates members with their workspaces and respective roles.
af_collab
& af_collab_member
tables: Denote collaborations and their constituent members.
Update tables: (af_collab_update
, af_collab_update_document
, etc.): Handle collaboration updates.
Statistics & snapshot tables: (af_collab_statistics
, af_collab_snapshot
, af_collab_state
): Track collaboration metrics and snapshots.
af_user_profile_view
view: Fetches the latest workspace_id for every user.
We've developed a tool designed to set up the database tables, triggers, and functions within a Supabase PostgreSQL database. With this tool, a single command can configure your database. For a detailed guide, please consult this documentation.
AppFlowy's backend is developed in Rust, which requires a Rust crate to interact seamlessly with the Postgres database.
Thankfully, the community has provided postgrest-rs, a crate specifically designed to handle Supabase's Postgres database features. We've defined a struct named SupabaseServer that implements the AppFlowyServer
trait.
When interacting with the Postgres database, primarily CRUD operations are carried out. However, it's important to emphasize the value of the RPC function for customizing database actions.
We've defined a function called flush_collab_updates_v3
. This function locks the row with a specified OID, aggregates the updates into a single update, and then deletes the consolidated updates.
Here is the definition of the flush_collab_updates_v3
function:
Then we can call the flush_collab_updates_v3
function in Rust.
With Supabase's realtime service, we can monitor changes to specific tables.
For instance, by watching the af_user
table, we can detect the latest user activities every time they sign in across different devices. Similarly, other tables can be monitored. This diagram illustrates how user profile updates are tracked across multiple devices.
We observe update events from both the af_collab_update
and af_user
tables. When an update event is triggered, we propagate the event data to the Rust backend. Different components will consume various updates.
A detailed discussion on this is beyond the scope of this documentation, but we will delve into it in future discussions.
File storage in AppFlowy is still under development.
Once implemented, users will be able to store images, videos, documents, and various other file types.
Self-hosting AppFlowy with Supabase has been made easy and straightforward. Follow this detailed guide:
Create a Supabase Project: Start by creating a new project in Supabase. Detailed instructions are available in the Supabase Getting Started Guide for Flutter.
Database Setup: Configure your Postgres database according to the steps provided in the Postgres Setup Guide.
Download AppFlowy: Get the latest release package from the AppFlowy Releases page.
Install and Launch: Install the downloaded AppFlowy application and open it.
Initial Setup: In the AppFlowy application, click on 'Quick Start'.
Cloud Settings: Go to the Settings
page, select Cloud Setting
from the sidebar, and choose Supabase
as your cloud provider.
Enter Details: Fill in your Supabase server's url
and anon key
, then click Restart
to apply the new settings.
User Settings: Access the Setting
page in the AppFlowy application and choose User
from the sidebar.
By following these steps, you'll be able to successfully set up and start using AppFlowy with Supabase as your backend.
🙏If you have any questions, don't hesitate to contact us on Discord.
Thanks for reading this article. If you have some time, please kindly take our 1-minute survey to give us feedback and let us know what interests you the most.
If you have a suggestion or question for self-hosted solutions or any part of AppFlowy, you can create an issue on GitHub and we’ll get back to you as soon as possible. Also, feel free to follow us @appflowy on Twitter or join our Discord server to follow our latest development!