Перейти к содержимому
Все материалы

MongoDB или PostgreSQL для Payload CMS: когда сложные блоки начинают влиять на выбор базы

27 октября 2026 г.•Александр Андреев

Payload CMS официально поддерживает и PostgreSQL, и MongoDB, причём на уровне самой CMS разница может быть почти незаметной: коллекции, поля, relationships, локализация и Blocks работают с обеими базами. Но чем сложнее становится структура контента, тем сильнее различается то, что происходит под Payload.

Свежая дискуссия в сообществе началась как раз с такого случая: разработчик, который обычно использует PostgreSQL, перевёл часть проектов Payload на MongoDB. Причины — постоянные изменения схемы в начале разработки и большое количество вложенных Blocks. После перехода на blocksAsJSON проблемы с миграциями, по его словам, тоже никуда не делись.

Обсуждение в r/PayloadCMS: https://www.reddit.com/r/PayloadCMS/comments/1xftitc/database_choice_mongo_vs_postgres/

Интересно здесь не выяснять, какая база «лучше». Сам Payload рекомендует выбирать базу исходя из структуры данных конкретного проекта. И сложные блочные страницы — один из случаев, когда этот выбор действительно начинает иметь значение.

Почему Blocks хорошо ложатся на MongoDB

Представим обычную страницу на Payload:

Page
├── title
├── slug
└── layout
    ├── Hero
    │   ├── heading
    │   ├── image
    │   └── buttons[]
    ├── Text
    │   └── content
    ├── Gallery
    │   └── images[]
    └── Products
        └── products[]

Для Payload это один документ Page.

MongoDB примерно так его и хранит: документ остаётся вложенной структурой, близкой к той, которую разработчик описал в Payload Config. Количество массивов, Blocks и локализованных полей не требует превращать каждый уровень вложенности в отдельную реляционную структуру.

Именно поэтому официальная документация Payload прямо перечисляет аргументы в пользу MongoDB: много динамических полей, много Arrays и Blocks, локализованный контент, а также нежелание синхронизировать production- и staging-базы через DDL-изменения.

Для CMS это довольно естественная модель: страница вместе со своими блоками во многих случаях действительно является единым объектом.

В PostgreSQL картина другая

PostgreSQL хранит данные реляционно. Payload использует Drizzle ORM и строит схему базы на основе конфигурации CMS.

На относительно плоской модели это удобно:

posts
authors
categories
products
orders

Особенно когда между сущностями много отношений и хочется получить ограничения целостности непосредственно на уровне базы.

Но сложный конструктор страниц приводит к заметно более разветвлённой реляционной схеме: Blocks, массивы, локализованные значения, relationships и версии должны каким-то образом отображаться на таблицы и связи. Большая Payload Config может породить гораздо более сложную физическую схему, чем кажется при взгляде на TypeScript.

Само по себе это не проблема: с этими таблицами напрямую разработчик вообще может никогда не работать.

Заметной сложность становится тогда, когда структура контента постоянно меняется.

Откуда берутся миграции

Допустим, сегодня блок выглядит так:

{
  slug: 'hero',
  fields: [
    { name: 'title', type: 'text' },
  ],
}

Через неделю в него добавили изображение, кнопки и варианты оформления. Потом разделили один блок на два. Потом удалили старое поле.

В PostgreSQL изменение Payload Config означает изменение схемы базы.

Во время локальной разработки Payload облегчает этот процесс: Drizzle push автоматически синхронизирует development-базу с конфигурацией, так что после каждого добавленного поля создавать миграцию не нужно. Документация прямо советует такой workflow: работать в development-режиме с push, а миграцию создавать после завершения очередного набора изменений.

Но production уже обновляется через миграции:

Payload Config
      ↓
migration
      ↓
PostgreSQL schema

И чем активнее меняется большая блочная схема, тем больше внимания требует этот процесс.

В MongoDB добавление поля в конфигурацию само по себе не требует DDL-миграции: старый документ может просто не содержать нового поля. Документация Payload формулирует это так: в реляционных базах миграции обязательны для окружений вне development, а с MongoDB миграции могут понадобиться изредка — или никогда.

Миграция всё же потребуется, когда существующие данные необходимо преобразовать из старой структуры в новую. Это принципиально другая модель разработки.

Но у MongoDB есть обратная сторона

Именно это стало главным возражением в обсуждении.

Если база не заставляет все документы соответствовать жёсткой схеме, со временем в ней могут одновременно жить документы разных поколений:

{
  "title": "Page",
  "hero": {
    "image": "..."
  }
}

и:

{
  "title": "Page",
  "hero": {
    "desktopImage": "...",
    "mobileImage": "..."
  }
}

Payload контролирует структуру данных на уровне приложения, но MongoDB не превращает изменение Payload Config в обязательную перестройку всех существующих документов.

Поэтому отсутствие DDL-миграций не означает отсутствия миграций как инженерной задачи. Если старые данные нужно привести к новой форме, это всё равно придётся сделать — просто в MongoDB разработчик сам решает, когда такое преобразование действительно необходимо. Именно так описывает миграции для MongoDB и официальная документация.

Получается компромисс: PostgreSQL заставляет заниматься изменениями схемы раньше, MongoDB позволяет отложить часть этой работы — но повышает риск накопить документы разной формы. Один из комментаторов дискуссии формулирует ровно это: с Mongo легко получить «кучу искажённых объектов», которые либо ломаются, либо требуют специальной обработки в коде.

Что такое blocksAsJSON

У PostgreSQL-адаптера Payload есть промежуточный вариант — blocksAsJSON:

postgresAdapter({
  blocksAsJSON: true,
})

Вместо реляционного представления Blocks Payload хранит их в JSON-колонке PostgreSQL. Документация прямо указывает, что такой режим может улучшить производительность проектов с большим количеством блоков.

Архитектурно это своего рода компромисс:

PostgreSQL

pages
├── id
├── title
├── slug
└── layout JSON
        ├── Hero
        ├── Text
        ├── Gallery
        └── ...

Основные сущности проекта остаются реляционными, relationships продолжают использовать преимущества PostgreSQL, но большое дерево блоков не раскладывается на множество таблиц.

Именно этот режим особенно интересен для крупных сайтов на Payload.

У blocksAsJSON тоже есть цена

JSON внутри PostgreSQL уже не обладает всеми свойствами обычной реляционной схемы.

Например, в текущей документации Payload отдельно предупреждают: запросы к JSON, Rich Text и blocksAsJSON не проходят через пользовательские query.operatorHandlers PostgreSQL-адаптера. Готовый пресет postgresUnaccent, делающий поиск без учёта диакритики, на такие поля не действует.

Кроме того, перевод существующего проекта на blocksAsJSON сам по себе требует преобразования данных.

Для этого Payload ещё в версии 3.73.0 добавил готовую предопределённую миграцию:

pnpm payload migrate:create \
  --file @payloadcms/db-postgres/blocks-as-json

Она предназначена именно для перевода существующей реляционной структуры Blocks в JSON-представление, а механизм --file в CLI Payload загружает такие готовые миграции прямо из пакетов @payloadcms/db-*.

Поэтому жалобу из свежего обсуждения на проблемы миграций с blocksAsJSON не стоит трактовать как доказанную проблему Payload: автор не привёл воспроизводимого примера, версии и деталей в треде нет. Но сам выбор между реляционным и JSON-хранением Blocks — реальный, и цена у этого выбора есть.

Когда MongoDB выглядит логичнее

Условный редакционный сайт может состоять почти целиком из документов:

Pages
Articles
Authors
Media

При этом Pages содержат большой page builder из десятков типов Blocks, много вложенных массивов и локализованного контента.

Для такого проекта MongoDB хорошо соответствует самой модели данных — и документация Payload прямо называет перечисленные признаки аргументами в её пользу.

Особенно это удобно на раннем этапе проекта, когда структура страниц ещё активно меняется.

А когда PostgreSQL выглядит естественнее

Теперь другая система:

Users
Customers
Orders
OrderItems
Products
Warehouses
Payments
Invoices

Здесь гораздо важнее отношения между сущностями: заказ принадлежит клиенту, позиция заказа — существующему товару, платёж — заказу. Нужна строгая целостность данных, а структура сущностей относительно стабильна.

Это естественная территория реляционной базы, и рекомендация Payload примерно такая же: PostgreSQL (или SQLite) стоит предпочитать, когда нужна целостность на уровне базы и в проекте много отношений между коллекциями.

При этом наличие нескольких Blocks на страницах совсем не означает, что от PostgreSQL нужно отказываться.

Третий вариант для многих проектов

Интереснее всего выглядит комбинация PostgreSQL + blocksAsJSON.

Основные сущности остаются реляционными:

users
products
categories
orders
pages

а внутри pages гибкая структура конструктора хранится как JSON. Это позволяет не превращать каждую деталь редакционной страницы в часть большой реляционной схемы, сохраняя PostgreSQL там, где его свойства действительно полезны.

По сути, разработчик разделяет два типа данных, которые часто живут внутри одного проекта Payload:

  • структурированные бизнес-данные — relational;
  • гибкий редакционный контент — JSON.

Для многих крупных проектов это может оказаться практичнее, чем выбирать базу исключительно по принципу «MongoDB или PostgreSQL».

Что выбрать для нового проекта

У Payload нет универсальной рекомендации использовать одну конкретную базу, и это принципиальная позиция документации: обе модели официально поддерживаются, а выбор должен зависеть от характера данных.

Если проект преимущественно контентный, схема часто меняется, много локализации, массивов и сложных Blocks — MongoDB действительно заслуживает рассмотрения.

Если Payload используется как основа приложения с большим количеством связанных сущностей и важна целостность на уровне базы — аргументы сильнее у PostgreSQL.

А если бизнес-данные реляционные, но страницы представляют собой огромные деревья Blocks, стоит отдельно посмотреть на blocksAsJSON.

Свежая дискуссия интересна именно этим. Она показывает: выбор базы для Payload определяется не размером проекта и даже не производительностью PostgreSQL или MongoDB в вакууме.

Гораздо важнее посмотреть на Payload Config и понять, какие данные вы на самом деле собираетесь в этой базе хранить.

Правило: выбор базы для Payload начинайте не с «что моднее», а с Payload Config: контентные деревья Blocks и локализация тянут к MongoDB (или blocksAsJSON в PostgreSQL), связанные бизнес-сущности и целостность — к PostgreSQL.

Источники

Обсуждение «Database choice: Mongo vs Postgres» в r/PayloadCMS: https://www.reddit.com/r/PayloadCMS/comments/1xftitc/database_choice_mongo_vs_postgres/

Документация Payload — Database Overview (когда предпочитать MongoDB, когда реляционную базу): https://payloadcms.com/docs/database/overview

Документация Payload — Postgres Adapter (blocksAsJSON, ограничения query.operatorHandlers): https://payloadcms.com/docs/database/postgres

Документация Payload — Migrations (MongoDB против реляционных баз, Drizzle push в dev): https://payloadcms.com/docs/database/migrations

Релиз Payload v3.73.0 с предопределённой миграцией blocksAsJSON: https://github.com/payloadcms/payload/releases/tag/v3.73.0

Назад в блог
MongoDB или PostgreSQL для Payload CMS: когда сложные блоки начинают влиять на выбор базы | Payload по-русски