LogoLogo
  • AppFlowy
    • ⭐Start here
      • Welcome to AppFlowy Docs
      • How to get help
      • Install AppFlowy
    • 🛠️Installation
      • 🖥️System Requirements
      • 💿Installation methods
        • Mac / Windows / Linux Packages
          • Installing on Linux
            • Installing & Setting up Flutter on Linux from Source
        • Docker
    • 🌱Community
      • 🤙Get in contact
      • 📔AppFlowy Mentorship Program
        • Program Guidance
        • Proposal Template
        • Pull Request Template
        • Mentorship 2023
          • Mentee Projects
            • Calendar View for AppFlowy Database
            • Custom Themes
            • Shortcuts and Customized Hotkeys for AppFlowy
            • Table
            • ⭐Favorites
            • Code Block
            • Outlines
            • Importers
            • AI Writers
            • Templates
          • Project Ideas
      • ✍️Write for AppFlowy
        • 📃Drafts
          • [Draft] Use Case: Software Engineer
          • [Draft] Use Case: High School Students
          • [Draft] How to add a new property to appflowy database
      • 🍂Hacktoberfest
    • 🛣️Roadmap
    • 🌋Product
      • 💽Data Storage
      • 🎨Customize and Style Content
      • ⏮️Duplicate, Delete, and Restore
      • 💎Databases
        • 🔢Database Properties
        • 🗃️Manage Properties
      • Ⓜ️Markdown
      • ⌨️Shortcuts
      • 🪄AppFlowy AI
      • 🦙AppFlowy Local AI - Ollama
      • 🎨Themes
      • ☁️AppFlowy Cloud
      • 🧩AppFlowy Plugins
        • Table-view Databases
        • Kanban Board
        • Calendar
        • Auto Generator
        • Smart Edit
        • Code Blocks
        • Math Equations
        • Cover
        • Emoji
  • Documentation
    • 💎Software Contributions
      • 🟢Get started
      • 💀Architecture
        • Frontend
          • Tauri
            • 🗺️CodeMap
          • Web
            • 🌟Design Philosophy
          • Flutter
            • 🗺️Project Structure: CodeMap
            • 🧮Grid
            • ⚙️Setting
          • Inter-Process Communication
          • User
            • User Data
            • Events & Notifications
          • Folder
            • Events & Notifications
          • Document
          • Database View
            • Events & Notifications
            • Grid
            • Calendar
            • Kanban Board
        • Backend
          • Initialize
          • Events
          • Delta(WIP)
          • Profiling
          • Database
        • Domain Driven Design
        • Proposals
      • 🏗️Conventions
        • 🔤Naming Conventions
        • ⌨️Code Conventions
          • 🐦Flutter
        • 🐙Git Conventions
      • 💛Submitting Code
        • 🏦Setting Up Your Repositories
        • ⤴️Submitting your first Pull Request
      • 🤟Coding Standards and Practices
        • 👽Rust Backend
    • 🚀AppFlowy
      • 👾How to contribute to AppFlowy
      • 🏗️Building from Source
        • 🌳Flutter Setup
          • 🐧Building on Linux
          • 🍎Building on macOS
          • 🪟Building on Windows
        • 🌐Web Setup
        • 📡Tauri Setup
      • ☁️Debugging with AppFlowy Cloud
      • 🔁Debugging in VS Code
      • ☎️Translate AppFlowy
      • ❓Troubleshooting
      • 👮‍♀️Licenses
    • 🏍️AppFlowy Editor
      • ⌨️How to Implement Markdown Syntax To Style Text In AppFlowy Editor
      • 🧩How to Create a Plugin for AppFlowy Editor
      • 👮‍♀️Licenses
    • ☁️AppFlowy Cloud
      • 🌈Architecture
      • ☀️Deployment
  • Guides
    • Sync Desktop and Mobile
    • Self-Hosting AppFlowy
      • ☁️Self-hosting AppFlowy with AppFlowy Cloud
      • 🆓Self-hosting AppFlowy for free Using Supabase
    • Import From Notion
  • Blog Highlights
    • 🔮Demystifying AppFlowy Editor's Codebase
  • Handbook
    • Core values
Powered by GitBook
On this page
  • The flowy-sqlite
  • Create schema
  • Update schema
  • Write Rust
  • Architecture

Was this helpful?

Edit on GitHub
  1. Documentation
  2. Software Contributions
  3. Architecture
  4. Backend

Database

PreviousProfilingNextDomain Driven Design

Last updated 2 years ago

Was this helpful?

AppFlowy use as database and as .

The flowy-sqlite

The crate, flowy-sqlite, contains the logic for creating the SQLite and providing a shared kv storage. It is located in frontend/rust-lib/flowy-sqlite.

cargo install diesel_cli --no-default-features --features sqlite

Create schema

Create a new schema.

/// Go to the working directory
cd frontend/rust-lib/flowy-sqlite/

/// Generate a new migration named user
diesel migration generate user

Output

  • Creating migrations/2022-08-07-140433_user/up.sql

  • Creating migrations/2022-08-07-140433_user/down.sql

Create a table named user_table. Open the up.sql

CREATE TABLE user_table (
    id TEXT NOT NULL PRIMARY KEY,
    name TEXT NOT NULL DEFAULT '',
    token TEXT NOT NULL DEFAULT '',
    email TEXT NOT NULL DEFAULT ''
);

When doing revert operation, the down.sql will be applied. We drop the user_table here.

DROP TABLE user_table;

Run the migration

diesel migration run

Migrations allow us to evolve the database schema over time. Each migration can be applied (up.sql) or reverted (down.sql). Applying and immediately reverting a migration should leave your database schema unchanged. It’s a good idea to make sure that down.sql is correct. You can quickly confirm that your down.sql rolls back your migration correctly by redoing the migration:

diesel migration redo

Output:

  • Rolling back migration 2022-08-07-140433_user

  • Running migration 2022-08-07-140433_user

Ok, here we go. Everything is fine. After running the migration, the schema is automatically added to the schema.rs

// flowy-sqlite/src/schema.rs
table! {
    user_table (id) {
        id -> Text,
        name -> Text,
        token -> Text,
        email -> Text,
    }
}

Writing Rust

#[derive(Clone, Default, Queryable, Identifiable, Insertable)]
#[table_name = "user_table"]
pub struct UserTable {
    pub(crate) id: String,
    pub(crate) name: String,
    pub(crate) token: String,
    pub(crate) email: String,
}

Update schema

Update an existing schema.

cd frontend/rust-lib/flowy-sqlite/
diesel migration generate user-add-icon

Output

  • Creating migrations/2022-08-07-140433_user-add-icon/up.sql

  • Creating migrations/2022-08-07-140433_user-add-icon/down.sql

up.sql

ALTER TABLE user_table ADD COLUMN icon_url TEXT NOT NULL DEFAULT '';

down.sql

ALTER TABLE user_table DROP COLUMN icon_url;
diesel migration run
diesel migration redo

After running the migration, the icon_url is added to the user_table schema automatically.

// flowy-sqlite/src/schema.rs
table! {
    user_table (id) {
        id -> Text,
        name -> Text,
        token -> Text,
        email -> Text,
        icon_url -> Text,
    }
}

Write Rust

We create a struct named UserTable to read the record of the user_table. The name of the properties should be the same as the user_table. We can use UserTable to insert a new record or read the existing record from the database.

#[derive(Clone, Default, Queryable, Identifiable, Insertable)]
#[table_name = "user_table"]
pub struct UserTable {
    pub(crate) id: String,
    pub(crate) name: String,
    pub(crate) token: String,
    pub(crate) email: String,
    pub(crate) icon_url: String,
}

Diesel provides lots of handy functions for reading and updating a record.

Read

// conn: the connection to the database
let user: UserTable = dsl::user_table
    .filter(user_table::id.eq(&user_id))
    .first::<UserTable>(conn)?;

Insert

// user: instance of the UserTable
let _ = diesel::insert_into(user_table::table)
            .values(user)
            .execute(conn)?;

Update

#[derive(AsChangeset, Identifiable, Default, Debug)]
#[table_name = "user_table"]
pub struct UserTableChangeset {
    pub id: String,
    pub name: Option<String>,
    pub email: Option<String>,
    pub icon_url: Option<String>
}

Apply the changeset to the database

// changeset: instance of the UserTableChangeset
diesel::update(user_table::table).set(&changeset);

Architecture

Traits are a name given to a group of functions that a data structure can implement. I think using traits to isolate dependencies is a very good practice.

The flowy-user dependencies on the flowy-sqlite crate directly. It initializes the database connection when the Application launch or when the user switches account. The flowy-grid defines the GridDatabase trait and the flowy-folder defines the WorkspaceDatabase trait, these two traits are implemented in the flowy-sdk crate.

flowy-sdk is a crate that aggregates all the crates and resolves each crate's dependencies. flowy-sqlite is a crate that handles all the grid operations

flowy-folder is a crate that handles all the folder operations. The folder represents the concepts that include the workspace, app, and view.

The following section will guide you through how to create or update a schema. Before starting, I recommend checking out the if you don't know about diesel before. Make sure you install the diesel CLI tool. You can install it by running:

Let's write some Rust to read the database data. We're not going to explain how to use the diesel macros here, you can check out for that.

Check out for more information about inserting a record.

We useAsChangeset macro that diesel provides to implement the AsChangeset trait. Check out for more information about updating a record.

We use dependency injection to forbid the other crates directly dependencies on the flowy-sqlite crate. Each crate defines their database to meet their need.

💎
💀
Diesel Getting Started
this
this
this
traits
SQLite
Diesel
ORM
schema
flowy-sqlite.png
file : database.plantuml