redberry/md-notion

Read your notion pages as Markdown in Laravel applications.

1.0.1 2025-10-03 13:30 UTC

This package is auto-updated.

Last update: 2025-10-03 13:35:48 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Read your notion pages as Markdown in Laravel applications

Example:

use Redberry\MdNotion\Facades\MdNotion;

$pageId = '263d9316605a806f9e95e1377a46ff3e';

// Get page content as markdown
$markdown = MdNotion::make($pageId)->content()->read();

// Get complete recursive content
$fullContent = MdNotion::make($pageId)->full();

Don't forget to star the repo ⭐

Table of contents

Installation

You can install the package via composer:

composer require redberry/md-notion

You can publish the config file with:

php artisan vendor:publish --tag="md-notion-config"

This is the contents of the published config file:

return [
    /**
     * The Notion API key used for authentication with the Notion API.
     */
    'notion_api_key' => env('NOTION_API_KEY', ''),

    /**
     * Defines the maximum block number that can be fetched in a single request.
     */
    'default_page_size' => env('NOTION_DEFAULT_PAGE_SIZE', 100),

    /**
     * Blade templates for markdown rendering
     */
    'templates' => [
        'page_markdown' => 'md-notion::page-md',
        'full_markdown' => 'md-notion::full-md',
    ],

    /**
     * Block type to adapter class mappings.
     * Customize these to use your own adapters.
     */
    'adapters' => [
        'paragraph' => \Redberry\MdNotion\Adapters\ParagraphAdapter::class,
        'heading_1' => \Redberry\MdNotion\Adapters\HeadingAdapter::class,
        // ... many more block adapters
    ],
];

Optionally, you can publish the views:

php artisan vendor:publish --tag="md-notion-views"

Configuration

Set your Notion API key in your .env file:

NOTION_API_KEY=your_notion_api_key_here

To get your Notion API key:

  1. Go to Notion Developers
  2. Create a new integration
  3. Copy the API key
  4. Add the integration to your Notion pages you want to read

Features

🔄 Fluent API - Chain methods for intuitive content fetching
📄 Page Reading - Extract Notion pages as clean markdown
🗃️ Database Support - Convert Notion databases to markdown tables
🌲 Recursive Fetching - Get all nested pages and databases in one call
🎨 Customizable Templates - Use Blade templates for markdown output
🧩 Custom Adapters - Extend block adapters for specialized content
Laravel Integration - Seamless service provider and facade support
🛠️ Configurable - Easy configuration via Laravel config files

Usage

Basic Page Content

Get the content of a single page as markdown:

use Redberry\MdNotion\Facades\MdNotion;

$pageId = '263d9316605a806f9e95e1377a46ff3e';
$content = MdNotion::make($pageId)->content()->read();

// Returns: "# Page Title\n\nPage content as markdown..."

Fetch Child Pages

Get collection of child pages:

$pages = MdNotion::make($pageId)->pages();

// Returns: Collection of Page objects

note: Each page has: id, title, content, created_time, last_edited_time, etc. Check API reference

Fetch Child Databases

Get collection of child databases:

$databases = MdNotion::make($pageId)->databases();

// Returns: Collection of Database objects

Each database has: id, title, description, properties, and table content, etc. Check API reference

Content with Children

Get page content including child pages and databases:

// Include child pages in Markdown content
$content = MdNotion::make($pageId)
    ->content()
    ->withPages()
    ->read();

// Include child databases as tables in Markdown content
$content = MdNotion::make($pageId)
    ->content()
    ->withDatabases()
    ->read();

// Include both child pages and databases in Markdown content
$content = MdNotion::make($pageId)
    ->content()
    ->withPages()
    ->withDatabases()
    ->read();

// Returns: Formatted markdown with main content + child content sections

Complete Recursive Content

Get everything recursively (current page + all nested pages and databases):

$fullContent = MdNotion::make($pageId)->full();

// Returns: Complete markdown with all nested content

full is the only method which by default returns database item's content as well. It performs minimum one request per page, so pages with nested content or big databases can hit the memory limits, can be slow or hit limits of notion API.

⚠️ WARNING: This may make many API requests for pages with deep nesting!

Dynamic Page Setting

Set page ID dynamically:

$mdNotion = MdNotion::make(); // Empty initially
$content = $mdNotion->setPage($pageId)->full();

Get Page Objects

Access the raw Page object with all data:

$page = MdNotion::make($pageId)
    ->content()
    ->withPages()
    ->withDatabases()
    ->get();

// Returns: Page object with loaded child content
// Access: $page->getTitle(), $page->getContent(), $page->getChildPages(), etc.

Page and Database Objects API

The MdNotion package provides rich object models for working with Notion pages and databases. Both Page and Database objects extend BaseObject and use several traits to provide comprehensive functionality.

Page Object API

Basic Properties and Methods

use Redberry\MdNotion\Objects\Page;

// Create from data
$page = Page::from([
    'id' => 'page-id-123',
    'title' => 'My Page Title',
    'content' => '# Page content...',
    'has_children' => true
]);

// Core properties
$page->getId();                    // string - Page ID
$page->getTitle();                 // string - Page title
$page->getContent();               // ?string - Page content
$page->hasContent();               // bool - Whether page has content
$page->hasChildren();              // bool - Whether page has child pages

Content Management

// Content operations
$page->setContent('# New content');
$page->getContent();               // Returns MD string: '# New content'
$page->hasContent();               // Returns: true

// Child database
$databases = $page->getChildDatabases();  // Collection<Database>

Fetching and Updating

When page is accessed as child page of another, it may not contain all information you need, including child pages, markdown content and etc. To get the needed data, you can use ID with MdNotion again or use fetch method on page instance.

// Fetch latest data from Notion API
$page->fetch();         // Updates current instance with fresh data

// The fetch method preserves object identity
$originalPage = Page::from(['id' => 'page-123']);
$updatedPage = $originalPage->fetch();
// $originalPage === $updatedPage (same page, updated data)

Database Object API

Basic Properties and Methods

use Redberry\MdNotion\Objects\Database;

// Create from data
$database = Database::from([
    'id' => 'db-id-123',
    'title' => 'My Database',
    'tableContent' => '| Name | Status |\n|------|--------|\n| Task 1 | Done |'
]);

// Core properties
$database->getId();                // string - Database ID
$database->getTitle();             // string - Database title
$database->getTableContent();      // ?string - Database as markdown table
$database->hasTableContent();      // bool - Whether has table content

Table Content Management

// Table content operations
$database->getTableContent();      // Returns markdown table string
$database->hasTableContent();      // Returns true if table content exists

// Read items content (populate child pages with content)
$database->readItemsContent();

Fetching and Updating

// Fetch latest data from Notion API
$database->fetch(); // Updates current instance with fresh data

Shared API (from BaseObject & Traits)

Both Page and Database objects share these APIs through inheritance and traits:

Title Management (HasTitle trait)

// Title operations
$object->getTitle();               // string - Get title
$object->setTitle('New Title');    // Set title
$object->renderTitle(1);           // string - Render as markdown heading (# Title)
$object->renderTitle(2);           // string - Render as level 2 heading (## Title)
$object->renderTitle(3);           // string - Render as level 3 heading (### Title)

Icon Management (HasIcon trait)

// Icon operations
$object->getIcon();                // ?array - Get icon data
$object->setIcon($iconData);       // Set icon data
$object->hasIcon();                // bool - Whether has icon
$object->processIcon();            // string - Get icon as emoji/markdown

// Icon types supported:
// - Emoji: Returns emoji character
// - External: Returns [IconName](url) markdown link
// - File: Returns [🔗](url) markdown link

Metadata Management (HasMeta trait)

// Timestamps
$object->getCreatedTime();         // string - ISO timestamp
$object->getLastEditedTime();      // string - ISO timestamp

// User information
$object->getCreatedBy();           // array - User data who created
$object->getLastEditedBy();        // array - User data who last edited

// Status flags
$object->isArchived();             // bool - Whether archived
$object->isTrashed();              // bool - Alias for isInTrash()

Parent Relationship (HasParent trait)

// Parent operations
$object->getParent();              // array - Full parent data
$object->hasParent();              // bool - Whether has parent
$object->getParentType();          // ?string - Parent type (page_id, workspace, etc.)
$object->getParentId();            // ?string - Parent ID based on type

Child Pages Management (HasChildPages trait)

// Child pages operations
$object->getChildPages();          // Collection<Page> - Child pages collection
$object->hasChildPages();          // bool - Whether has child pages

URLs and Properties

// URL management
$object->getUrl();                 // ?string - Notion URL
$object->hasUrl();                 // bool - Whether has URL
$object->getPublicUrl();           // ?string - Public sharing URL
$object->hasPublicUrl();           // bool - Whether has public URL

// Properties management
$object->getProperties();          // array - All Notion properties
$object->hasProperties();          // bool - Whether has properties
$object->getProperty('title');     // mixed - Get specific property

Serialization and Data Conversion

// Convert to array
$data = $object->toArray();        // Complete object data as array

// Fill from array (merge new data)
$object->fill($newData);           // Updates object with new data, preserves existing

// Static creation
$object = Page::from($data);       // Create new instance from data array
$object = Database::from($data);   // Create new instance from data array

Advanced Usage Examples

Working with Object Identity

// Objects maintain identity during fetch operations
$page = Page::from(['id' => 'page-123']);
$samePageReference = $page->fetch();

// $page === $samePageReference (same object instance)
// But $page now has updated content from Notion API

Child Content Management

// Page with child databases
$childDbs = $page->getChildDatabases();

// Database with child pages
$childPages = $database->getChildPages();

// Read all child page content
$database->readItemsContent();

Partial Data Updates

$page = Page::from([
    'id' => 'page-123',
    'title' => 'Original Title',
    'content' => 'Original content'
]);

// Partial update - only updates specified fields
$page->fill([
    'title' => 'Updated Title'
    // content remains "Original content"
]);

echo $page->getTitle();   // "Updated Title"
echo $page->getContent(); // "Original content" (preserved)

Customization

Custom Blade Templates

You can customize how markdown is rendered by creating your own Blade templates:

Create your custom templates

To change the layout or logic how read or full methods render the markdown, you should create a new view and replace them in config:

// config/md-notion.php
return [
    'templates' => [
        'page_markdown' => 'custom.page-template',     // For content()->read()
        'full_markdown' => 'custom.full-template',     // For full()
    ],
];

When customizing templates, you have access to these variables:

For page templates (content()->read()):
  • $current_page - Array with title, content, hasContent
  • $child_databases - Array of database data with title, table_content, hasTableContent
  • $child_pages - Array of page data with title, content, hasContent
  • $withDatabases - Boolean flag
  • $withPages - Boolean flag
  • $hasChildDatabases - Boolean flag
  • $hasChildPages - Boolean flag
For full templates (full()):
  • Complete recursive data structure with nested current_page, child_databases, child_pages
  • Each level includes hasChildDatabases, hasChildPages, level for depth tracking

You can check current blade templates here:

  • read() Method: resources\views\page-md.blade.php
  • full() Method: resources\views\full-md.blade.php

Custom Block Adapters

Create custom adapters to handle specific Notion block types:

1. Create a custom adapter:

You will need adapter class extending src\Adapters\BaseBlockAdapter.php and custom blade template rendering the data.

// app/Adapters/CustomCodeAdapter.php
<?php

namespace App\Adapters;

use Redberry\MdNotion\Adapters\BaseBlockAdapter;

class CustomCodeAdapter extends BaseBlockAdapter
{
    public function getType(): string
    {
        return 'code'; // Set the type
    }

    public function getTemplate(): string
    {
        return 'notion.blocks.code'; // Set blade view
    }

    // Main method which prepares data to pass to view
    protected function prepareData(array $block): array
    {
        $code = $block['code'];

        $content = $this->processRichText($code['richText']);
        $content = str_replace('\\n', "\n", $content);

        // You will have access to this variables in your blade template:
        return [
            'content' => $content,
            'language' => $code['language'],
            'caption' => $this->processRichText($dto->caption),
            'block' => $code,
        ];
    }
}

Check example: src\Adapters\ParagraphAdapter.php

2. Register your adapter in the configuration:

// config/md-notion.php
return [
    'adapters' => [
        'callout' => \App\Adapters\CustomCalloutAdapter::class,
        // Keep existing adapters...
        'paragraph' => \Redberry\MdNotion\Adapters\ParagraphAdapter::class,
        'heading_1' => \Redberry\MdNotion\Adapters\HeadingAdapter::class,
        // ... other adapters
    ],
];

Testing

composer test

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

Thanks to following people and projects:

License

The MIT License (MIT). Please see License File for more information.