Notion is a collaborative workspace that combines documents, databases, wikis, and project management in a single tool. With 30+ million users, Notion unique block-based architecture and flexible database system create distinctive system design challenges. This guide covers the block data model, database engine, collaboration, and the API that makes Notion extensible.
Block-Based Data Model
Everything in Notion is a block. A page is a block. A paragraph is a block. A heading, image, to-do item, embed, and database are all blocks. Blocks can contain other blocks (nesting). Data model: block: block_id (UUID), type (text, heading, image, database, page, toggle, callout, etc.), parent_id (the block this is nested under), properties (type-specific: text content, image URL, checkbox state), children (ordered list of child block_ids), created_by, created_at, last_edited_by, last_edited_at. The entire workspace is a tree of blocks. A page is a root block containing child blocks (paragraphs, headings, images). A database is a block whose children are database rows (each row is also a block/page). This recursive structure is powerful: a database cell can contain rich text with nested blocks, a toggle can contain a full document, and pages can be nested infinitely. Storage: blocks are stored in a database (PostgreSQL or similar) keyed by block_id. The parent-child relationship is stored as parent_id on each block. Retrieving a page: fetch the root block, then recursively fetch children. Optimization: batch-fetch children in a single query using IN clause or a recursive CTE.
Notion Databases
A Notion database is a collection of pages (rows) with typed properties (columns). Property types: text, number, select (dropdown), multi-select, date, person, relation (link to another database), formula, rollup (aggregate from related records), and more. Each row is a full page — clicking a row opens it as a page with rich content. Database views: the same data can be displayed as: table (spreadsheet-like), board (Kanban — grouped by a select property), list, calendar (positioned by date property), gallery (visual cards), and timeline (Gantt chart by date range). Each view has its own: filter rules, sort order, visible properties, and group-by settings. All views share the same underlying data. Implementation: the database is stored as a collection of blocks (one per row) with structured properties. Each property value is stored as: (block_id, property_id, value, type). Queries: fetching a table view = SELECT * FROM properties WHERE database_id = X, apply filters, sort, and paginate. Filters: the filter engine supports compound conditions: (property A = “In Progress” AND property B > 100) OR property C contains “urgent.” Implemented as a predicate tree evaluated against each row. For large databases (10,000+ rows): index commonly filtered properties. Notion uses a custom query engine optimized for the block-property model.
Real-Time Collaboration
Notion supports real-time collaborative editing similar to Google Docs. Multiple users can edit the same page simultaneously. Architecture: CRDT-inspired conflict resolution at the block level. Each block property change is an operation: set(block_id, property, value, timestamp, user_id). Last-writer-wins per property per block. If User A changes a paragraph text and User B changes the same paragraph simultaneously: the last timestamp wins. Unlike character-level CRDTs (Google Docs), Notion operates at the block level. Two users editing different blocks in the same page have no conflict. Two users editing the same block: last-writer-wins on the specific property. Text within a block: Notion uses a character-level CRDT for rich text content within a single text block. This allows two users to type in the same paragraph without overwriting each other. Structural operations: adding, removing, and reordering blocks. These are tree operations (insert child, remove child, move child). Notion uses a tree CRDT that handles concurrent structural edits: if User A adds a block after block X and User B deletes block X simultaneously, A block is orphaned and reparented to X parent. WebSocket: all editors of a page maintain WebSocket connections to the collaboration service. Operations are broadcast in real-time. Presence: cursor positions and active blocks are shared among editors.
Permissions and Sharing
Notion has a hierarchical permission model: permissions are set on pages/databases and inherited by all child blocks. Permission levels: full access, can edit, can comment, can view. Sharing targets: specific users, groups, the entire workspace, or anyone with a link (public). Inheritance: a page nested inside a shared page inherits the parent permissions. Explicit permissions on a child page override inherited ones. “Lock page” prevents edits by non-owners (useful for templates and documentation that should not be accidentally modified). Guest access: external users (outside the workspace) can be invited to specific pages. They see only the shared content, not the broader workspace. Workspace structure: personal pages (visible only to the owner), shared pages (visible to invited users), and teamspaces (visible to all workspace members). The permission check on every block access: (1) Check if the block page has explicit permissions for the user. (2) If not, walk up the parent chain until finding a page with permissions. (3) Apply the most specific permission found. Caching: resolve and cache the effective permission per user per page in Redis. Invalidate when permissions change on any ancestor page.
API and Integrations
The Notion API enables programmatic access to workspace content. Core endpoints: (1) Pages — create, read, update pages. A page is a block with child blocks. (2) Databases — query databases with filters, sorts, and pagination. Create/update database rows (pages with properties). (3) Blocks — CRUD on individual blocks. Append children to a page. (4) Search — full-text search across the workspace. (5) Users — list workspace members. Authentication: OAuth 2.0 for third-party integrations. Internal integrations use a secret token scoped to a workspace. Rate limits: 3 requests per second per integration (conservative to protect the platform). Pagination: cursor-based for all list endpoints. Use cases: (1) Sync external data into Notion databases (import Jira tickets, GitHub issues). (2) Automate workflows (when a database row status changes to “Done,” update a project tracker). (3) Build custom views (render Notion database data in a custom dashboard). (4) Content publishing (export Notion pages as blog posts). Webhooks (limited): Notion does not yet have full webhook support. Integrations poll for changes using the search endpoint with a filter on last_edited_time. This is less efficient than event-driven webhooks but functional for most use cases.
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”How does Notion block-based architecture work?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Everything in Notion is a block: pages, paragraphs, headings, images, databases, to-dos. Blocks contain other blocks (nesting). Data model: block_id, type, parent_id, properties (type-specific), children (ordered list). The entire workspace is a tree. A page is a root block with child blocks. A database is a block whose children are rows (each row is also a page). This recursive structure enables: database cells containing rich text with nested blocks, toggles containing full documents, and infinite page nesting. Storage: blocks in PostgreSQL keyed by block_id. Parent-child via parent_id. Retrieving a page: fetch root block, batch-fetch children with IN clause or recursive CTE. The block model is Notion key differentiator — it makes the same data model serve documents, databases, wikis, and project management.”}},{“@type”:”Question”,”name”:”How does Notion handle real-time collaboration at the block level?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Notion uses CRDT-inspired conflict resolution operating at the block level. Each operation: set(block_id, property, value, timestamp, user_id). Last-writer-wins per property per block. Two users editing different blocks = no conflict. Same block, different properties = no conflict. Same property = last timestamp wins. For text within a single block: Notion uses a character-level CRDT allowing two users to type in the same paragraph without overwriting. Structural operations (add/remove/reorder blocks) use a tree CRDT handling concurrent inserts and deletes. WebSocket connections broadcast operations in real-time. Presence (cursor positions, active blocks) is shared as ephemeral state. This block-level granularity is coarser than Google Docs (character-level) but simpler and sufficient for Notion use case where most edits target different blocks.”}}]}