PocketBaseБэкендSQLite

PocketBase: бэкенд в одном файле. Когда его хватит, а когда нет

6 мин чтения

PocketBase это готовый бэкенд в одном файле. Запустили его, и у приложения есть база, вход пользователей и загрузка файлов. Ниже о том, как он устроен и когда его хватит.

Что внутри

PocketBase написан на Go и распространяется под лицензией MIT. Внутри:

  • база SQLite в режиме WAL, файл лежит в каталоге pb_data;
  • REST API для каждой коллекции, с фильтрами, сортировкой и пагинацией;
  • realtime: подписка на изменения записей через Server-Sent Events;
  • вход пользователей: email и пароль, одноразовые коды на почту, двухфакторный вход, OAuth2 (Google, Apple, GitHub, Яндекс и ещё около 30 провайдеров) и произвольный OpenID Connect;
  • файлы: поля-файлы в записях, миниатюры картинок, хранение на диске или в S3;
  • админка по адресу /_/: коллекции, записи, настройки, логи запросов.

Официальные SDK есть для JavaScript и Dart (Flutter). Для остальных языков подходит обычный HTTP.

Коллекции и правила доступа

Данные хранятся в коллекциях трёх типов: base для обычных данных, auth для пользователей и view для представлений на основе SQL-запроса.

У каждой коллекции пять правил: listRule, viewRule, createRule, updateRule, deleteRule. Правило это фильтр, который проверяется на каждый запрос:

  • null (правило не задано): доступ только у суперпользователя;
  • пустая строка: доступ у всех, включая анонимов;
  • выражение: доступ, если выражение истинно.

Типичное правило «пользователь видит только свои записи»:

@request.auth.id != "" && owner = @request.auth.id

Новые коллекции создаются с закрытыми правилами. Это безопасное поведение по умолчанию: данные не утекут, пока правила не открыты явно.

Пример на JavaScript

import PocketBase from "pocketbase";

const pb = new PocketBase("https://pb.example.ru");

// вход
await pb.collection("users").authWithPassword("anna@example.ru", "secret");

// создать запись
await pb.collection("tasks").create({
  title: "Позвонить клиенту",
  owner: pb.authStore.record.id,
  done: false,
});

// список с фильтром и сортировкой
const page = await pb.collection("tasks").getList(1, 20, {
  filter: "done = false",
  sort: "-created",
});

// подписка на изменения
pb.collection("tasks").subscribe("*", (e) => {
  console.log(e.action, e.record);
});

SDK хранит токен в pb.authStore и подставляет его в каждый запрос.

Своя логика без отдельного сервера

Код на JavaScript кладётся в каталог pb_hooks рядом с pb_data. Файлы с расширением .pb.js подхватываются при старте.

// pb_hooks/main.pb.js

// проверка перед созданием записи
onRecordCreate((e) => {
  if (e.record.get("title").length > 200) {
    throw new BadRequestError("Слишком длинный заголовок");
  }
  e.next();
}, "tasks");

// свой эндпоинт
routerAdd("GET", "/api/health", (e) => {
  return e.json(200, { ok: true });
});

// задача по расписанию: каждый час
cronAdd("cleanup", "0 * * * *", () => {
  // удалить устаревшие черновики
});

Для сложной логики PocketBase подключается как Go-библиотека: тогда весь бэкенд собирается в один бинарник со своим кодом.

Где предел

Запись. SQLite допускает одну пишущую транзакцию в момент времени. Чтение идёт параллельно и быстро. Для MVP, внутренних инструментов, мобильных приложений и ботов этого достаточно. Сервис с тысячами записей в секунду из многих потоков упрётся в запись раньше, чем в процессор.

Один сервер. Встроенной репликации и кластера нет. Рост только за счёт ресурсов сервера.

Версия до 1.0. Разработчики прямо предупреждают: до версии 1.0 обратная совместимость не гарантируется. Перед обновлением стоит читать changelog. Переход на v0.23 в 2024 году, например, поменял API хуков и SDK.

Нет полноценного SQL снаружи. Сложные отчёты и аналитику удобнее строить на Postgres.

Бэкапы

В админке, в разделе настроек бэкапов, есть встроенное резервное копирование: архив pb_data по расписанию (cron-выражение), с хранением локально или в S3. Минимальная схема для продакшена:

  1. Бэкап по расписанию раз в сутки.
  2. Хранение в S3, а не только на том же диске.
  3. Проверка восстановления: развернуть архив на чистом PocketBase и открыть админку.

Перенос PocketBase на другой сервер устроен так же: копируется каталог pb_data целиком.

Когда PocketBase, а когда Supabase

КритерийPocketBaseSupabase
БазаSQLitePostgres
Памятьдесятки мегабайтот 4 GB для self-hosted
Сервисоводин процессоколо 13 контейнеров
SQL и расширенияограниченнополный Postgres, pgvector, PostGIS
Масштабодин серверодин сервер в self-hosted, больше в облаке
ПодходитMVP, внутренние инструменты, мобильные приложенияпродукты с данными на Postgres и ростом

Если приложение уже написано и нужен только бэкенд для данных, входа и файлов, PocketBase запускается быстрее всех. Если данные будут расти и понадобится SQL, сразу стоит смотреть на Supabase или Postgres со своим API.

Читать дальше