Разработка серверной части (Backend) для мобильного приложения на Node.js
Мобильное приложение без бэкенда — это заметки офлайн. Как только появляются пользователи, синхронизация данных, push-уведомления, платежи — нужен сервер. Node.js + TypeScript — прагматичный выбор для большинства мобильных бэкендов: быстрый старт, JSON-нативность, огромная экосистема, один язык для команды если на фронте тоже TypeScript.
Стек и его компоненты
Fastify или Express. Express — привычно и документировано везде. Fastify — быстрее (в тестах на 20–30% выше throughput), схема-first валидация через JSON Schema, TypeScript-поддержка из коробки. Для нового проекта выбираем Fastify.
// Fastify + TypeScript + Zod валидация
import Fastify from 'fastify';
import { z } from 'zod';
const app = Fastify({ logger: true });
const CreatePostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
authorId: z.string().uuid(),
});
app.post('/posts', async (request, reply) => {
const body = CreatePostSchema.parse(request.body);
const post = await postService.create(body);
return reply.status(201).send(post);
});
PostgreSQL + Prisma ORM. Prisma даёт типобезопасные запросы из TypeScript-схемы:
model User {
id String @id @default(uuid())
email String @unique
posts Post[]
createdAt DateTime @default(now())
}
const user = await prisma.user.findUnique({
where: { id: userId },
include: { posts: { take: 10, orderBy: { createdAt: 'desc' } } },
});
Prisma Studio — встроенный GUI для просмотра данных в разработке. Prisma Migrate — версионирование схемы БД через migrations-файлы.
Аутентификация мобильных клиентов
Для мобильных приложений — JWT с коротким access token (15 мин) и долгим refresh token (30 дней) в secure storage на устройстве. Ротация refresh token при каждом использовании.
import jwt from 'jsonwebtoken';
export const generateTokens = (userId: string) => ({
accessToken: jwt.sign({ sub: userId, type: 'access' }, process.env.JWT_SECRET!, {
expiresIn: '15m',
}),
refreshToken: jwt.sign({ sub: userId, type: 'refresh' }, process.env.JWT_REFRESH_SECRET!, {
expiresIn: '30d',
}),
});
// Middleware верификации
export const authMiddleware = async (request: FastifyRequest, reply: FastifyReply) => {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) return reply.status(401).send({ error: 'Unauthorized' });
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
request.userId = payload.sub!;
} catch {
return reply.status(401).send({ error: 'Invalid token' });
}
};
Refresh token хранится в БД (таблица refresh_tokens) — это позволяет инвалидировать все сессии пользователя при смене пароля или logout с всех устройств.
Push-уведомления через Firebase Admin SDK
import * as admin from 'firebase-admin';
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });
export const sendPushNotification = async (
fcmToken: string,
title: string,
body: string,
data?: Record<string, string>
) => {
const message: admin.messaging.Message = {
token: fcmToken,
notification: { title, body },
data,
apns: {
payload: { aps: { sound: 'default', badge: 1 } },
},
android: {
priority: 'high',
notification: { sound: 'default' },
},
};
return admin.messaging().send(message);
};
Для массовых рассылок — sendEachForMulticast с батчами по 500 токенов. Невалидные токены (messaging/registration-token-not-registered) удаляем из БД.
Realtime: WebSocket или Server-Sent Events
Чат, лайки в реальном времени, статус доставки — требуют постоянного соединения. WebSocket через ws или socket.io:
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ server: httpServer });
const connections = new Map<string, WebSocket>();
wss.on('connection', (ws, request) => {
const userId = getUserIdFromRequest(request);
connections.set(userId, ws);
ws.on('close', () => connections.delete(userId));
});
export const notifyUser = (userId: string, event: object) => {
const ws = connections.get(userId);
if (ws?.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(event));
}
};
Для горизонтального масштабирования (несколько инстансов) WebSocket-соединение привязано к конкретному серверу — нужен Redis Pub/Sub для рассылки между инстансами.
Инфраструктура
Docker + docker-compose для локальной разработки. PM2 или Docker для production. PostgreSQL основная БД, Redis для кеша и очередей (BullMQ). S3 для файлов.
API-структура:
src/
modules/
users/ # controller, service, repository, dto
posts/
notifications/
shared/
middleware/
guards/
utils/
app.ts
server.ts
Слоистость: controller → service → repository. Service не знает о HTTP-контексте — тестируется чисто через Jest.
Типичные ошибки
Блокирующие операции в event loop. JSON.parse большого файла, синхронный fs.readFileSync на каждый запрос — блокируют все остальные запросы. Тяжёлые операции — в worker threads или offload в очередь.
N+1 в Prisma. Запрос списка постов без include → отдельный запрос для автора каждого поста. Решение: всегда include или Prisma $queryRaw с JOIN для сложных случаев.
Отсутствие rate limiting. Мобильный клиент отправляет 100 запросов в секунду при плохом сетевом коде — сервер ложится. Fastify Rate Limit (@fastify/rate-limit) + Redis для распределённого ограничения.
Что входит в разработку
Проектирование API контракта (OpenAPI/Swagger). Настройка инфраструктуры (БД, Redis, Docker). Реализация Auth (JWT + refresh). CRUD модули под требования приложения. Push-уведомления. CI/CD конфигурация. Документация API.
Сроки
MVP бэкенд (Auth + 3–5 ресурсов + push): 2–3 недели. Полноценный бэкенд с realtime, платежами, файлами: 1–3 месяца. Стоимость рассчитывается после проработки функциональных требований.







