# How GraphQL Powers AFFiNE's Backend Services: Architecture and Implementation

> Discover how AFFINE leverages a centralized GraphQL API for type-safe data fetching, automatic error handling, and reactive UI updates across its backend services.

- Repository: [Toeverything/AFFiNE](https://github.com/toeverything/AFFiNE)
- Tags: architecture
- Published: 2026-03-05

---

**AFFiNE uses a centralized GraphQL API layer to handle all client-server communication, enabling type-safe data fetching, automatic error handling, and reactive UI updates through a single `/graphql` endpoint.**

The open-source AFFiNE repository relies on GraphQL as the primary communication protocol between its frontend modules and backend services. By implementing a dedicated `GraphQLService` class and leveraging auto-generated TypeScript types from `@affine/graphql`, AFFiNE ensures that every data request—from user profile updates to AI transcription jobs—maintains strict type safety and consistent error handling across the entire application stack.

## The GraphQLService Architecture

At the heart of AFFiNE's GraphQL implementation lies the `GraphQLService` class located in [`packages/frontend/core/src/modules/cloud/services/graphql.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/cloud/services/graphql.ts). This service functions as the exclusive bridge between frontend modules and the backend, encapsulating all network logic, authentication refresh mechanisms, and error normalization.

### Core Implementation Details

The `GraphQLService` exposes two primary methods for executing operations: `gql()` for standard promise-based requests and `rxGql()` for reactive Observable streams. Both methods accept a `QueryOptions` object containing the GraphQL document and variables, then forward the request to `gqlFetcherFactory('/graphql', fetcher.fetch)`.

```typescript
// packages/frontend/core/src/modules/cloud/services/graphql.ts
class GraphQLService {
  async gql<T>(options: QueryOptions): Promise<T> {
    // Executes via gqlFetcherFactory and returns typed data
  }
  
  rxGql<T>(options: QueryOptions): Observable<T> {
    // Wraps the promise in an RxJS Observable for reactive UIs
  }
}

```

### Error Handling and Authentication

The service implements sophisticated error normalization by catching server-side errors and converting them into `UserFriendlyError` instances that UI layers can present directly to users. When the backend returns a 401 (unauthenticated) response, `GraphQLService` automatically triggers session re-validation, ensuring seamless authentication recovery without manual intervention from individual store implementations.

### Reactive Data with RxJS

For UI components requiring real-time updates, the `rxGql` method returns an RxJS `Observable` built on top of the underlying promise-based fetch. This enables frontend stores to subscribe to data changes and automatically update views when GraphQL responses arrive, particularly useful for long-running operations like AI transcription jobs.

## Type-Safe GraphQL Operations in AFFiNE

AFFiNE generates TypeScript types from its GraphQL schema into the `@affine/graphql` package, ensuring complete type safety across the client-server boundary. Frontend modules import these generated types and use them with `GraphQLService` to eliminate runtime errors from malformed queries.

### Fetching User Profile Data

The following example from [`packages/frontend/core/src/modules/cloud/stores/user-quota.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/cloud/stores/user-quota.ts) demonstrates how `UserQuotaStore` retrieves quota information using generated GraphQL types:

```typescript
// packages/frontend/core/src/modules/cloud/stores/user-quota.ts
import { getCurrentUserProfileQuery } from '@affine/graphql';
import type { GraphQLService } from '../services/graphql';

export class UserQuotaStore {
  constructor(private readonly graphqlService: GraphQLService) {}

  async loadQuota() {
    const data = await this.graphqlService.gql({
      query: getCurrentUserProfileQuery,
      variables: {},
    });
    // data.currentUserProfile is fully typed
    return data.currentUserProfile;
  }
}

```

### Reactive Implementation

The same query can be converted to an Observable for reactive UI updates:

```typescript
// Reactive version for automatic UI updates
const quota$ = this.graphqlService.rxGql({
  query: getCurrentUserProfileQuery,
  variables: {},
});

quota$.subscribe(({ data }) => {
  // UI updates automatically when response arrives
});

```

## Key Use Cases Across AFFiNE's Backend

GraphQL serves as the unified communication layer for virtually every backend interaction in AFFiNE. The following modules demonstrate the breadth of GraphQL integration:

- **User Settings Management** – [`packages/frontend/core/src/modules/cloud/stores/user-settings.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/cloud/stores/user-settings.ts) uses `GraphQLService` to persist user preferences and application configuration to the backend.

- **Permission Enforcement** – [`packages/frontend/core/src/modules/permissions/entities/permission.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/permissions/entities/permission.ts) leverages generated GraphQL types to validate user capabilities and workspace access levels.

- **Notification Systems** – [`packages/frontend/core/src/modules/notification/stores/notification.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/notification/stores/notification.ts) fetches real-time notification data through GraphQL queries to alert users of collaborative events.

- **AI Transcription Jobs** – [`packages/frontend/core/src/modules/media/entities/audio-transcription-job-store.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/media/entities/audio-transcription-job-store.ts) demonstrates GraphQL-driven status polling for long-running AI operations, using reactive Observables to track job completion.

- **Workspace Synchronization** – [`packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts) implements the core logic for synchronizing workspace state with the server via GraphQL mutations and subscriptions.

## Why AFFiNE Chose GraphQL for Backend Services

The decision to standardize on GraphQL addresses several architectural requirements for a complex knowledge management application:

**Selective Data Fetching** – Frontend components request exactly the fields they need, eliminating over-fetching common in REST APIs. This is critical for performance when loading large workspace metadata or permission lists.

**Strong Typing** – Generated TypeScript types from `@affine/graphql` ensure that client and server contracts remain synchronized. Changes to the schema propagate compile-time errors to the frontend, preventing runtime mismatches.

**Single Endpoint Architecture** – All backend operations—CRUD operations, authentication flows, AI job scheduling, and real-time subscriptions—share the same `/graphql` endpoint. This simplifies network configuration, CDN caching rules, and deployment topology.

## Summary

- AFFiNE implements a centralized **GraphQLService** in [`packages/frontend/core/src/modules/cloud/services/graphql.ts`](https://github.com/toeverything/AFFiNE/blob/main/packages/frontend/core/src/modules/cloud/services/graphql.ts) that serves as the exclusive gateway for all backend communication.
- The service provides both **promise-based** (`gql`) and **reactive** (`rxGql`) methods, supporting both standard async operations and RxJS Observable streams for real-time UI updates.
- **Type safety** is enforced through generated TypeScript types from `@affine/graphql`, eliminating runtime errors and ensuring client-server contract consistency.
- GraphQL powers diverse backend features including **user quotas**, **permissions**, **notifications**, **AI transcription jobs**, and **workspace synchronization** through a single `/graphql` endpoint.
- The architecture enables **selective data fetching**, **automatic authentication refresh**, and **unified error handling** via `UserFriendlyError` conversion.

## Frequently Asked Questions

### How does AFFiNE handle authentication errors in GraphQL requests?

When the backend returns a 401 (unauthenticated) response, the `GraphQLService` automatically detects this status and triggers session re-validation. This occurs within the error handling layer of the service, ensuring that individual stores and UI components don't need to implement manual retry logic for authentication failures.

### What is the difference between `gql` and `rxGql` methods in AFFiNE's GraphQLService?

The `gql` method returns a standard JavaScript Promise suitable for one-off data fetching operations, while `rxGql` returns an RxJS Observable that enables reactive programming patterns. The Observable version is particularly useful for UI components that need to automatically update when data changes or for polling long-running operations like AI transcription jobs.

### How does AFFiNE ensure type safety between its GraphQL schema and frontend code?

AFFiNE generates TypeScript types from its GraphQL schema into the `@affine/graphql` package. Frontend modules import these generated types and use them with the `GraphQLService`, ensuring that query variables and response data are fully typed at compile time. This prevents runtime errors from malformed queries and keeps client-server contracts synchronized.

### Which backend features in AFFiNE rely on GraphQL for data communication?

Virtually all backend interactions in AFFiNE use GraphQL, including user profile and quota management, workspace synchronization, permission enforcement, notification systems, and AI-assisted features like audio transcription. These operations are centralized through the `GraphQLService` and communicate via a single `/graphql` endpoint, providing a unified interface for all server-side functionality.