Skip to content
STACK IT FAST

AFFiNE

Curated OSSHybridCollaborative Workspace6-20 people

Audited from github.com/toeverything/AFFiNE

AFFiNE is an open-source, local-first knowledge workspace that merges documents, whiteboards and databases into one block-based canvas, available as web, desktop (Electron) and mobile apps with optional cloud sync.

Language
TypeScript
Database
PostgreSQL
Hosting
Docker
Running for
2 years 6 months
Team
6-20 people

Why this architecture

A single Yjs CRDT model lets docs, canvas and tables share one editor and sync engine; local-first storage in SQLite/IndexedDB keeps the app usable offline while a NestJS GraphQL server adds collaboration and cloud features.

Tech stack by layer

15 technologies · audited Sep 17, 2026
Frontend & UI
  • ReactApplication shell in `packages/frontend/core`, routed with `react-router-dom`; state with Jotai and RxJS via `@toeverything/infra`.
  • TypeScriptAll web, desktop and mobile packages; `typecheck` runs `tsc -b` over the whole workspace.
  • BlockSuiteIn-repo block editor framework (`blocksuite/`) that renders docs, whiteboard and database blocks on one canvas; built on Lit web components.
  • YjsCRDT document model shared by editor, sync layer and server (`yjs`, `y-protocols`).
  • vanilla-extractZero-runtime CSS for components alongside Emotion and the `@toeverything/theme` tokens.
  • ElectronDesktop app in `packages/frontend/apps/electron` packaged with Electron Forge (dmg, deb, squirrel, flatpak, AppImage makers) and `electron-updater`.
  • CapacitoriOS and Android apps wrap the web core with `@capacitor/ios` and `@capacitor/android`.
Backend & APIs
  • NestJSServer in `packages/backend/server` on Express with Nest modules for GraphQL, websockets, scheduling and throttling.
  • GraphQLPrimary API via `@nestjs/graphql` and Apollo Server; a shared `@affine/graphql` package holds the client documents.
  • Socket.IORealtime doc sync over `@nestjs/platform-socket.io` with the Redis adapter for multi-instance deployments.
  • RustNative modules via napi-rs for the server and desktop (`packages/*/native`), plus `uniffi` bindings for the mobile apps.
  • OpenTelemetryTracing and metrics instrumentation for HTTP, GraphQL, Nest, ioredis and Socket.IO with Prometheus and Zipkin exporters.
Data & Persistence
  • PostgreSQLServer database via Prisma; `PLpgSQL` in the language stats comes from the migration SQL. Render config provisions a managed Postgres.
  • PrismaORM and migrations for the server (`prisma`, `@prisma/client`, `data-migration` script).
  • RedisCache, pub/sub and the Socket.IO adapter (`ioredis`); the Rust workspace also depends on the `redis` crate.
  • SQLiteLocal-first storage on desktop and mobile through the Rust `nbstore`/`sqlite_v1` crates using `sqlx`; browsers use IndexedDB (`idb`).
  • S3Blob storage behind an S3-compatible adapter in `packages/common/s3-compat`.
Infrastructure & Deploy
  • DockerOfficial images built by `build-images.yml`; self-host compose files live in `.docker/selfhost`.
  • KubernetesHelm chart and deployment manifests under `.github/helm` and `.github/deployment` for the cloud release.
  • Render`render.yaml` deploys the Docker image as a web service with a Postgres database, a key-value store and a persistent disk.
  • SentryError reporting in web (`@sentry/react`), desktop (`@sentry/electron`) and the tracking package.
Tooling, Testing & Ops
  • VitestUnit tests for frontend packages; the server uses `ava` with `supertest` and `c8` coverage.
  • PlaywrightEnd-to-end suites under `tests/` for local, cloud, desktop and mobile targets.
  • oxlintLinting and formatting with `oxlint`/`oxfmt`, `taplo` for TOML and `cargo fmt` for Rust through lint-staged.
  • GitHub ActionsBuild/test, image builds, and separate release pipelines for cloud, desktop and mobile.

AFFiNE architecture diagram

Open SVG
AFFiNE architecture diagramWeb app → Server (GraphQL); Web app → Doc sync (Yjs updates); Desktop & mobile → Local store (local-first); Desktop & mobile → Doc sync (Yjs updates); Server → PostgreSQL (Prisma); Doc sync → Redis (adapter); Server → Blob storage (blobs); Server → Native modules (hot paths)CLIENTSSERVICESWORKERS & JOBSDATA & STORAGEWeb appReact · BlockSuiteDesktop & mobileElectron · CapacitorServerNestJS · GraphQLDoc syncSocket.IONative modulesRust · napi-rsLocal storeSQLite (nbstore)PostgreSQLPrismaRediscache · pub/subBlob storageS3-compatibleGraphQLYjs updatesYjs updateshot pathsPrismaadapterblobslocal-first
How the main components of AFFiNE connect, drawn from the audited repository.
Diagram as text
  • Web app (React · BlockSuite) → Server (NestJS · GraphQL): GraphQL
  • Web app (React · BlockSuite) → Doc sync (Socket.IO): Yjs updates
  • Desktop & mobile (Electron · Capacitor) → Local store (SQLite (nbstore)): local-first
  • Desktop & mobile (Electron · Capacitor) → Doc sync (Socket.IO): Yjs updates
  • Server (NestJS · GraphQL) → PostgreSQL (Prisma): Prisma
  • Doc sync (Socket.IO) → Redis (cache · pub/sub): adapter
  • Server (NestJS · GraphQL) → Blob storage (S3-compatible): blobs
  • Server (NestJS · GraphQL) → Native modules (Rust · napi-rs): hot paths

Key architectural decisions

6 decisions
  1. 01

    One CRDT document model for docs, whiteboard and tables

    Everything is a Yjs document edited through the in-repo BlockSuite framework (blocksuite/), so a page can mix text blocks, an edgeless canvas and database views without separate data models. yjs appears in the server, the core app and even the Electron main process.

  2. 02

    Local-first storage, with the server as a sync peer

    Desktop and mobile persist documents in SQLite through the Rust nbstore crate (packages/frontend/native/nbstore), browsers use IndexedDB, and the NestJS server syncs updates over Socket.IO and stores them in PostgreSQL. The app stays usable offline and the server is optional for a single user.

  3. 03

    Rust for the hot paths, exposed through napi-rs and uniffi

    The Cargo workspace builds native modules for the server (packages/backend/native), the desktop app (packages/frontend/native) and mobile (packages/frontend/mobile-native). napi bridges Node/Electron, uniffi generates Swift and Kotlin bindings, which is why those languages show up in the repository.

  4. 04

    A single web core reused by web, Electron and Capacitor shells

    packages/frontend/core holds the product; apps/web, apps/electron-renderer, apps/ios, apps/android and apps/mobile are thin shells that add platform APIs (@affine/electron-api, @capacitor/*).

  5. 05

    GraphQL-first server with heavy observability

    The NestJS server exposes Apollo GraphQL plus Socket.IO gateways and ships OpenTelemetry instrumentation for HTTP, GraphQL, Redis and sockets with Prometheus and Zipkin exporters, reflecting that the same code powers the hosted AFFiNE Cloud.

  6. 06

    Dual licensing signals in the repository

    GitHub reports the license as "Other" while the root package.json declares MIT and both LICENSE and LICENSE-MIT files exist. Check both files before reusing code.

How AFFiNE is built

How AFFiNE is structured

AFFiNE is a Yarn workspace on Node 22 (.yarnrc.yml, engines.node >=22.12) with a Cargo workspace alongside it. The root package.json lists the workspace groups: blocksuite/**/* (the editor framework), packages/*/* (backend, common and frontend libraries), packages/frontend/apps/* (the shells), tools/* and tests/*. A custom CLI (yarn affine …, from tools/cli) drives dev, build and init.

  • blocksuite/framework and blocksuite/affine implement the block editor: docs, the edgeless whiteboard and database views are all blocks on one canvas.
  • packages/frontend/core is the product UI; packages/frontend/component is the design system with Storybook; i18n, routes, track and templates are supporting libraries.
  • packages/frontend/apps/* are shells: web, electron + electron-renderer, ios, android, mobile and mobile-shared.
  • packages/backend/server is the NestJS server; packages/backend/native its Rust companion.
  • packages/common/* holds shared code such as auth, graphql, infra, nbstore, realtime, reader and s3-compat.
  • Cargo.toml declares the Rust members: backend/native, common/native, frontend/native (with nbstore, schema, sqlite_v1) and frontend/mobile-native.

Frontend

The UI is React with react-router-dom, Jotai atoms and RxJS-based services from @toeverything/infra. Styling mixes vanilla-extract, Emotion and the @toeverything/theme tokens; Radix UI primitives, cmdk and sonner provide the interaction layer. The editor itself is BlockSuite, built on Lit web components and wired to Yjs documents (yjs, y-protocols). Rendering extras include mermaid, katex, shiki and a Typst renderer, and @toeverything/pdf-viewer handles attachments. The browser build stores data in IndexedDB (idb) and talks to the server with GraphQL and socket.io-client.

Desktop is Electron packaged by Electron Forge with makers for macOS, Windows, Debian, Flatpak and AppImage, auto-updates via electron-updater, and native capabilities from @affine/native. Mobile apps are Capacitor shells (@capacitor/ios, @capacitor/android) around the same core, with Rust bindings generated by uniffi.

Backend & APIs

packages/backend/server is a NestJS application on Express. The API is GraphQL through @nestjs/graphql and Apollo Server with graphql-upload for files; websockets run through @nestjs/platform-socket.io with @socket.io/redis-adapter. Cross-cutting concerns use @nestjs/throttler, @nestjs/schedule, nestjs-cls (with a Prisma transactional adapter), winston logging and piscina worker threads. Auth uses jose/jsonwebtoken with @node-rs/argon2 hashing; email is rendered with react-email and sent via nodemailer. The server ships a full OpenTelemetry setup (HTTP, GraphQL, Nest, ioredis and Socket.IO instrumentation; Prometheus, Zipkin and Google Cloud Trace exporters), and exa-js plus tiktoken in the native package support the Copilot features.

Data & persistence

Server data lives in PostgreSQL through Prisma (prisma, @prisma/client, a data-migration script); the migration SQL is why PLpgSQL appears in the language stats. Redis (ioredis) provides caching and the Socket.IO adapter. On desktop and mobile, documents persist locally in SQLite through the Rust nbstore and sqlite_v1 crates using sqlx; in the browser, IndexedDB. Blobs go to S3-compatible storage through packages/common/s3-compat.

Realtime / collaboration

Every document is a Yjs CRDT, so edits from multiple clients merge deterministically. Clients exchange updates with the server over Socket.IO; the Redis adapter lets several server instances share rooms. The packages/common/realtime package and the Rust y-octo crate (a Yjs implementation in Rust) support the sync layer on both sides.

Build, test & deploy

  • Linting is oxlint and oxfmt (with taplo for TOML and cargo fmt for Rust) through husky and lint-staged; typecheck runs tsc -b.
  • Tests: Vitest for frontend packages, ava + supertest for the server, Playwright suites in tests/affine-local, affine-cloud, affine-desktop and affine-mobile; coverage goes to Codecov (codecov.yml).
  • CI: build-test.yml, build-images.yml, copilot-test.yml; releases are split into release.yml, release-cloud.yml, release-desktop.yml and release-mobile.yml, with scripts/set-version.sh and tools/changelog supporting the process.

Self-hosting notes

Official images are built by build-images.yml, and .docker/selfhost contains the compose setup for a self-hosted server. render.yaml is a ready blueprint: it runs the Docker image as a web service with DATABASE_URL from a managed Postgres, REDIS_SERVER_* from a key-value service, a /info health check and a persistent disk mounted at /root/.affine for uploaded blobs and the generated private key. A Helm chart under .github/helm targets Kubernetes. Because clients are local-first, the server only needs to be reachable when you want sync, sharing or cloud features.

What to copy (and what not to)

Copy the "one CRDT document, many views" idea if your product blends docs and canvases; it removes a whole class of data-sync bugs. Copy the thin-shell pattern for Electron and Capacitor around a single web core. Be careful copying the Rust layer: it pays off here because storage, media and mobile bindings are shared across four platforms, but it doubles the toolchain (Cargo, napi-rs, uniffi) for a team that only ships a web app.

Sources & repo audit

Audited
Sep 17, 2026
Commit
2af3077
License
NOASSERTION

Independent analysis of repository at github.com/toeverything/AFFiNE. Spotted an inaccuracy? Use the claim form to request a correction.

Show this stack on your README or website
Tech stack of AFFiNE
[![Tech stack of AFFiNE on STACK IT FAST](https://stackitfast.com/badge/affine.svg)](https://stackitfast.com/project/affine)

Paste it into your README. It shows your current stack and links to this page.

Use this stack

Scaffold it with your agent

Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with AFFiNE's architecture.

  1. 1Copy the promptThe full markdown spec, with every layer and decision.
  2. 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
  3. 3Paste and scaffoldUse it as the first instruction; review before you ship.
use-this-stack.md · 61 lines · 9.0 KB
# MISSION: Scaffold "AFFiNE" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **AFFiNE**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: AFFiNE
- **What It Does**: AFFiNE is an open-source, local-first knowledge workspace that merges documents, whiteboards and databases into one block-based canvas, available as web, desktop (Electron) and mobile apps with optional cloud sync.
- **Domain & Category**: Collaborative Workspace
- **Production Scale**: 6-20 people
- **Development Mode**: HYBRID
- **Architectural Rationale**: A single Yjs CRDT model lets docs, canvas and tables share one editor and sync engine; local-first storage in SQLite/IndexedDB keeps the app usable offline while a NestJS GraphQL server adds collaboration and cloud features.
- **Live Website Reference**: https://affine.pro
- **Source Repository**: https://github.com/toeverything/AFFiNE
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: TypeScript, Rust, React, Yjs, NestJS, GraphQL, Prisma, PostgreSQL, Redis, Socket.IO, SQLite, Electron, Capacitor, Docker, Kubernetes
- **Primary Language(s)**: TypeScript, Rust
- **License of the reference repo**: NOASSERTION
- **Frontend**: React — Application shell in `packages/frontend/core`, routed with `react-router-dom`; state with Jotai and RxJS via `@toeverything/infra`.; TypeScript — All web, desktop and mobile packages; `typecheck` runs `tsc -b` over the whole workspace.; BlockSuite — In-repo block editor framework (`blocksuite/`) that renders docs, whiteboard and database blocks on one canvas; built on Lit web components.; Yjs — CRDT document model shared by editor, sync layer and server (`yjs`, `y-protocols`).; vanilla-extract — Zero-runtime CSS for components alongside Emotion and the `@toeverything/theme` tokens.; Electron — Desktop app in `packages/frontend/apps/electron` packaged with Electron Forge (dmg, deb, squirrel, flatpak, AppImage makers) and `electron-updater`.; Capacitor — iOS and Android apps wrap the web core with `@capacitor/ios` and `@capacitor/android`.
- **Backend & APIs**: NestJS — Server in `packages/backend/server` on Express with Nest modules for GraphQL, websockets, scheduling and throttling.; GraphQL — Primary API via `@nestjs/graphql` and Apollo Server; a shared `@affine/graphql` package holds the client documents.; Socket.IO — Realtime doc sync over `@nestjs/platform-socket.io` with the Redis adapter for multi-instance deployments.; Rust — Native modules via napi-rs for the server and desktop (`packages/*/native`), plus `uniffi` bindings for the mobile apps.; OpenTelemetry — Tracing and metrics instrumentation for HTTP, GraphQL, Nest, ioredis and Socket.IO with Prometheus and Zipkin exporters.
- **Data & persistence**: PostgreSQL — Server database via Prisma; `PLpgSQL` in the language stats comes from the migration SQL. Render config provisions a managed Postgres.; Prisma — ORM and migrations for the server (`prisma`, `@prisma/client`, `data-migration` script).; Redis — Cache, pub/sub and the Socket.IO adapter (`ioredis`); the Rust workspace also depends on the `redis` crate.; SQLite — Local-first storage on desktop and mobile through the Rust `nbstore`/`sqlite_v1` crates using `sqlx`; browsers use IndexedDB (`idb`).; S3 — Blob storage behind an S3-compatible adapter in `packages/common/s3-compat`.
- **Infrastructure & deploy**: Docker — Official images built by `build-images.yml`; self-host compose files live in `.docker/selfhost`.; Kubernetes — Helm chart and deployment manifests under `.github/helm` and `.github/deployment` for the cloud release.; Render — `render.yaml` deploys the Docker image as a web service with a Postgres database, a key-value store and a persistent disk.; Sentry — Error reporting in web (`@sentry/react`), desktop (`@sentry/electron`) and the tracking package.
- **Tooling, testing & ops**: Vitest — Unit tests for frontend packages; the server uses `ava` with `supertest` and `c8` coverage.; Playwright — End-to-end suites under `tests/` for local, cloud, desktop and mobile targets.; oxlint — Linting and formatting with `oxlint`/`oxfmt`, `taplo` for TOML and `cargo fmt` for Rust through lint-staged.; GitHub Actions — Build/test, image builds, and separate release pipelines for cloud, desktop and mobile.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/toeverything/AFFiNE @ 2af3077)
1. **One CRDT document model for docs, whiteboard and tables**: Everything is a Yjs document edited through the in-repo BlockSuite framework (`blocksuite/`), so a page can mix text blocks, an edgeless canvas and database views without separate data models. `yjs` appears in the server, the core app and even the Electron main process.
2. **Local-first storage, with the server as a sync peer**: Desktop and mobile persist documents in SQLite through the Rust `nbstore` crate (`packages/frontend/native/nbstore`), browsers use IndexedDB, and the NestJS server syncs updates over Socket.IO and stores them in PostgreSQL. The app stays usable offline and the server is optional for a single user.
3. **Rust for the hot paths, exposed through napi-rs and uniffi**: The Cargo workspace builds native modules for the server (`packages/backend/native`), the desktop app (`packages/frontend/native`) and mobile (`packages/frontend/mobile-native`). `napi` bridges Node/Electron, `uniffi` generates Swift and Kotlin bindings, which is why those languages show up in the repository.
4. **A single web core reused by web, Electron and Capacitor shells**: `packages/frontend/core` holds the product; `apps/web`, `apps/electron-renderer`, `apps/ios`, `apps/android` and `apps/mobile` are thin shells that add platform APIs (`@affine/electron-api`, `@capacitor/*`).
5. **GraphQL-first server with heavy observability**: The NestJS server exposes Apollo GraphQL plus Socket.IO gateways and ships OpenTelemetry instrumentation for HTTP, GraphQL, Redis and sockets with Prometheus and Zipkin exporters, reflecting that the same code powers the hosted AFFiNE Cloud.
6. **Dual licensing signals in the repository**: GitHub reports the license as "Other" while the root `package.json` declares MIT and both `LICENSE` and `LICENSE-MIT` files exist. Check both files before reusing code.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
   - Structure as a Turborepo monorepo with strict package boundaries:
     - `apps/web`: Application UI, routing, layouts, and server endpoints.
     - `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
     - `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
     - `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
   - Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
   - Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
   - Isolate interactive UI state to leaf components. Keep core pages lightweight and performant.
4. **Design System & Aesthetics**:
   - Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
   - Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
   - Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
   - Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (PostgreSQL, Prisma, Redis, SQLite, S3): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.
Scaffolded something with this prompt?
Would you pick this stack for a collaborative workspace project?

Frequently asked about AFFiNE

What is AFFiNE built with?

AFFiNE is a TypeScript and Rust monorepo. The web app is React on top of the in-repo BlockSuite editor framework with Yjs CRDT documents; the server is NestJS with Apollo GraphQL, Prisma on PostgreSQL, Redis and Socket.IO; desktop uses Electron and mobile uses Capacitor, with Rust native modules bridged through napi-rs and uniffi.

Does AFFiNE work offline or without a server?

Yes. It is local-first: documents are stored in SQLite on desktop and mobile through the Rust nbstore crate and in IndexedDB in the browser. The server acts as a sync and collaboration peer and is optional for single-user use.

How does AFFiNE sync and collaborate in real time?

Documents are Yjs CRDTs, so concurrent edits merge without conflicts. Updates travel over Socket.IO to the NestJS server, which uses the Redis adapter to fan out between instances and persists updates in PostgreSQL.

Can I self-host AFFiNE?

Yes. The repository ships official Docker images, self-host compose files under .docker/selfhost, a Helm chart under .github/helm, and a render.yaml blueprint that provisions the web service, PostgreSQL and a Redis-compatible store on Render.

Why does AFFiNE use Rust?

Performance-sensitive and platform-specific parts live in a Cargo workspace: local SQLite storage, document loading and import, audio and media handling, and a Rust Yjs implementation (y-octo). napi-rs exposes them to Node and Electron; uniffi generates Swift and Kotlin bindings for the mobile apps.

One email a month: new deep dives and stack trends

New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.

use-this-stack.md