How AppFlowy Implements Data Import from Notion, Todoist, and Other Tools

AppFlowy implements data import through a cross-layer pipeline where the Flutter UI packages raw files into protobuf-encoded requests and hands them to a Rust backend that parses the data and creates workspace views, with specific handlers for Notion ZIP archives and extensible support for CSV-based tools like Todoist.

AppFlowy’s import architecture separates data ingestion from data processing to keep the codebase modular and extensible. According to the AppFlowy-IO/AppFlowy source code, the Flutter frontend never parses third-party formats directly; instead, it builds ImportItemPayloadPB or ImportZipPB objects and dispatches them via the FolderEvent API to Rust handlers that perform the heavy lifting.

Architectural Overview of the Import Pipeline

The import system spans four distinct layers, each with specific responsibilities and well-defined interfaces:

  • Flutter UI Layer – Displays the import dialog, handles file selection, and constructs protobuf payloads. Key components include ImportPanel, ImportType enum, and NotionImporter located in frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart.
  • Flutter Service Layer – Sends protobuf requests to the backend via the FolderEvent API. The ImportBackendService class in frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart provides importPages() and importZipFiles() methods.
  • Protobuf Definitions – Define the wire format for all import payloads. The ImportItemPayloadPB, ImportZipPB, and ImportTypePB structs live in frontend/rust-lib/flowy-folder/src/entities/import.rs.
  • Rust Backend Layer – Parses payloads, reads files or raw bytes, and converts data into internal ImportItem structs. The import_pages_handler and import_zip_file_handler functions in frontend/rust-lib/flowy-folder/src/event_handler.rs receive these events and delegate to FolderManager.import() and FolderManager.import_zip_file() for insertion into the workspace hierarchy.

This separation ensures that adding a new third-party source requires only a new UI entry and potentially a new parser in Rust, without touching the core folder management logic.

How Notion Data Import Works

Notion imports rely on the platform’s ZIP export format, which contains JSON page definitions and associated assets.

ZIP File Processing Pipeline

When a user initiates a Notion import:

  1. The NotionImporter widget in _sidebar_import_notion.dart captures the ZIP file path selected by the user.
  2. The UI constructs an ImportZipPB protobuf message with the file_path field set to the selected archive.
  3. ImportBackendService.importZipFiles() dispatches the FolderEventImportZipFile event to the Rust backend.
  4. The import_zip_file_handler in event_handler.rs receives the payload and calls FolderManager.import_zip_file(&data.file_path).
  5. The Rust backend extracts the archive, traverses the Notion JSON structure, and transforms each page into an ImportItem with appropriate view layouts and metadata.
  6. Each ImportItem is persisted as a View inside the user’s workspace. Errors are wrapped in FlowyError and propagated back to the UI for display.

Key Source Files for Notion Import

  • UI Widget: _sidebar_import_notion.dart – Handles file picker interaction and error display.
  • Service Interface: import_service.dart – Defines ImportBackendService.importZipFiles().
  • Protobuf Schema: import.rs – Contains ImportZipPB and related type definitions.
  • Event Handler: event_handler.rs – Implements import_zip_file_handler and routes to the folder manager.

Extending AppFlowy for Todoist Data Import

As of the current main branch, AppFlowy does not ship a built-in Todoist importer, but the existing CSV pipeline provides a foundation for implementing one with minimal changes:

  • UI Extension – Add a todoist variant to the ImportType enum in import_type.dart and register a corresponding entry in ImportPanel.
  • Payload Construction – Todoist exports data as CSV. The UI can reuse the existing CSV path by creating an ImportItemPayloadPB with importType set to ImportTypePB.CSV and encoding the file bytes.
  • Backend Parsing – Extend the Rust ImportItem conversion logic in the folder manager to detect the Todoist CSV schema, mapping rows to checklist items and creating views with TodoList block layouts.

Because FolderEventImportData and import_pages_handler already handle CSV parsing, no new event handlers are required for basic Todoist support; only the schema-specific transformation logic needs implementation.

Importing from Other Tools: CSV, Markdown, and AFDatabase

The generic import panel in import_panel.dart supports several additional sources through the same ImportBackendService.importPages endpoint, varying only the ImportItemPayloadPB.import_type field:

  • History Document (.afdoc) – Migrates legacy AppFlowy documents via ImportTypePB.HistoryDocument.
  • History Database (.afdb) – Imports archived database files as ImportTypePB.HistoryDatabase.
  • Markdown and Text (.md, .txt) – Converts Markdown content to AppFlowy documents using ImportTypePB.Markdown.
  • CSV (.csv) – Creates grid views from tabular data via ImportTypePB.CSV.
  • AFDatabase (.afdb) – Imports native AppFlowy database exports.

For each type, the UI reads the file as a string or byte array, constructs the appropriate ImportItemPayloadPB, and sends it through the FolderEventImportData channel to the import_pages_handler in Rust.

Practical Implementation: Code Examples

Triggering a Notion Import Programmatically

To initiate a Notion import from Dart code without using the default UI:

final notionZipPath = '/path/to/notion-export.zip';

final result = await ImportBackendService.importZipFiles([
  ImportZipPB()..filePath = notionZipPath,
]);

result.fold(
  (_) => debugPrint('Notion import succeeded'),
  (err) => debugPrint('Notion import failed: ${err.msg}'),
);

This call creates an ImportZipPB, transmits it to the Rust backend, and returns a FlowyResult containing either void success or a FlowyError with diagnostic details.

Extending the Import Type Enum for Todoist

To add Todoist support, modify the Dart layer as follows:

// In import_type.dart
enum ImportType {
  // existing entries...
  todoist,

  List<String> get allowedExtensions {
    switch (this) {
      case ImportType.todoist:
        return ['csv'];
      // other cases...
    }
  }
}

// In import_panel.dart
case ImportType.todoist:
  final data = await File(path).readAsString();
  importValues.add(
    ImportItemPayloadPB.create()
      ..name = name
      ..data = utf8.encode(data)
      ..viewLayout = ViewLayoutPB.Grid
      ..importType = ImportTypePB.CSV,
  );
  break;

Because the Rust backend already handles ImportTypePB.CSV, this implementation immediately enables basic Todoist CSV ingestion. For richer metadata mapping (e.g., labels to tags), extend ImportTypePB in import.rs and add corresponding transformation logic in the folder manager.

Summary

  • Notion imports utilize a specialized ZIP pipeline: ImportZipPB → FolderEventImportZipFile → import_zip_file_handler → FolderManager.import_zip_file, which extracts JSON and assets to create workspace views.
  • Todoist and similar tools can leverage the existing CSV infrastructure by adding UI variants and reusing ImportItemPayloadPB with ImportTypePB.CSV, minimizing backend changes.
  • Generic imports for Markdown, CSV, and AppFlowy-specific formats flow through ImportItemPayloadPB → FolderEventImportData → import_pages_handler → FolderManager.import.
  • The architecture’s separation of concerns allows new third-party integrations by adding protobuf enum values and Rust parsing logic without modifying core UI components.

Frequently Asked Questions

Does AppFlowy support native Todoist imports?

No, AppFlowy does not currently ship with a built-in Todoist importer. However, the existing CSV import pipeline in import_service.dart and the generic ImportItemPayloadPB structure provide the necessary infrastructure to add Todoist support by extending the ImportType enum and handling Todoist's CSV export format in the Rust backend.

What file formats can AppFlowy import from other tools?

AppFlowy supports importing Notion (ZIP archives), Markdown (.md, .txt), CSV (.csv), History Documents (.afdoc), and History Databases (.afdb). Each format maps to a specific ImportTypePB enum value defined in frontend/rust-lib/flowy-folder/src/entities/import.rs, with the backend containing dedicated logic for extracting content from each file type.

How does the Rust backend handle Notion's ZIP structure?

The import_zip_file_handler in event_handler.rs receives the ZIP file path, delegates extraction to FolderManager.import_zip_file(), and walks the nested JSON files to construct ImportItem structs. These structs capture page hierarchies, content blocks, and embedded assets, which are then persisted as native AppFlowy View objects with appropriate layout metadata.

Can imports be triggered programmatically without the UI?

Yes, developers can call ImportBackendService.importPages() or ImportBackendService.importZipFiles() directly from Dart code, passing constructed ImportItemPayloadPB or ImportZipPB objects. This bypasses the ImportPanel UI and allows automated migration scripts or third-party integrations to ingest data into AppFlowy workspaces.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →