Настройка 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 pagination (LIMIT 20 OFFSET 200) деградирует на больших объёмах — PostgreSQL всё равно читает 220 строк. Cursor-based pagination быстрее:
-- Cursor pagination по (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 недели. Стоимость рассчитывается индивидуально.







