Налаштування PostgreSQL для мобільного додатку
Мобільний додаток не підключається до PostgreSQL напрямки. Прямій зв'язок з БД із клієнта — це антипаттерн: учітні дані від БД у коді додатку, відсутність авторизаційного прошарку, уразливість до SQL-інъєкцій через клієнтський код. PostgreSQL у контексті мобільної розробки — це бекенд, до якого додаток звертається через API. Завдання тут — правильно налаштувати стек, забезпечити типобезопасні запити та уникнути N+1 проблем.
Архітектура взаємодії
Mobile App → REST/GraphQL API → Backend (Node/Go/Laravel/etc.) → PostgreSQL
Бекенд — обов'язковий прошарок. Вибір ORM та драйвера на стороні сервера впливає на продуктивність мобільного додатку непрямо, але ощутимо: погані запити = повільні відповіді = погана UX.
Типічні проблеми на бекенді, які впливають на мобільний клієнт
N+1 запити. GET /api/products повертає 100 продуктів. Кожен продукт вимагає категорію — 100 окремих SELECT. Всього 101 запит замість одного JOIN. Мобільний клієнт чекає 2 секунди замість 100 мс.
На Node.js з Prisma — явна eager loading через include:
const products = await prisma.product.findMany({
where: { categoryId, isActive: true },
include: {
category: { select: { id: true, name: true, slug: true } },
images: { take: 1, orderBy: { sortOrder: 'asc' } },
_count: { select: { reviews: true } }
},
orderBy: { createdAt: 'desc' },
take: 20,
skip: offset
})
Відсутність індексів. SELECT по неіндексованому полю на таблиці з 1 млн рядків — sequential scan. На малих даних невидимо, у production — timeout.
-- Індекси для типичних мобільних запитів
CREATE INDEX CONCURRENTLY idx_products_category_active
ON products(category_id, is_active)
WHERE is_active = true;
CREATE INDEX CONCURRENTLY idx_orders_user_created
ON orders(user_id, created_at DESC);
-- Для повнотекстового пошуку
CREATE INDEX idx_products_search
ON products USING gin(to_tsvector('russian', title || ' ' || description));
CONCURRENTLY — індекс створюється без блокування таблиці. У production це обов'язково.
Важкі відповіді. API повертає повні об'єкти зі всіма полями. Мобільний клієнт отримує 50 KB JSON там, де потрібно 5 KB. Явні SELECT-проекції замість SELECT *:
// Тільки потрібні поля для списку
const productsListDto = await prisma.product.findMany({
select: {
id: true,
title: true,
priceCents: true,
thumbnailUrl: true,
averageRating: true,
reviewsCount: true
// НЕ: description (довгий текст), rawData, adminNotes
}
})
Пагінація
Offset пагінація (LIMIT 20 OFFSET 200) деградує на великих обсягах — PostgreSQL все одно читає 220 рядків. Cursor-based пагінація швидше:
-- Cursor пагінація за (created_at, id)
SELECT * FROM products
WHERE (created_at, id) < ($last_created_at, $last_id)
AND category_id = $category_id
AND is_active = true
ORDER BY created_at DESC, id DESC
LIMIT 20;
На мобільному клієнті — нескінченний скролл через Paging 3 (Android) або UICollectionView DiffableDataSource (iOS), отримуючи cursor із попередньої відповіді.
Real-time оновлення
PostgreSQL LISTEN/NOTIFY + WebSocket/SSE на бекенді → push оновлень на мобільний клієнт. Для чатів, live-сповіщень, real-time дашбордів.
// Бекенд: підписка на PostgreSQL NOTIFY
const client = await pool.connect()
await client.query('LISTEN product_updates')
client.on('notification', (msg) => {
const payload = JSON.parse(msg.payload)
// Рассилаємо через WebSocket всім клієнтам, підписаним на цю категорію
broadcastToSubscribers(payload.categoryId, payload)
})
// Тригер на PostgreSQL
CREATE FUNCTION notify_product_change() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('product_updates',
json_build_object('id', NEW.id, 'categoryId', NEW.category_id)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
На мобільному клієнті — WebSocket через Starscream (iOS) або OkHttp WebSocket (Android), оновлює локальний Room/SQLite кеш при отриманні події.
Connection pooling
Мобільні додатки роблять багато коротких запитів. Без пуллінгу кожен запит створює нове з'єднання з PostgreSQL — дорого. PgBouncer перед PostgreSQL або вбудований пул у ORM (Prisma $connectionPoolSize, Sequelize pool.max):
Mobile clients → API servers (N instances) → PgBouncer → PostgreSQL
transaction mode, 25 connections на сервер
У трансакційному режимі PgBouncer з'єднання займано тільки на час транзакції — десятки API-серверів працюють через малий пул реальних з'єднань.
Налаштування бекенда з PostgreSQL, індексами, пагінацією та пулінгом для мобільного API: 1–2 тижні. Вартість рассчитывается індивідуально.







