Знаете Flutter? Значит, можете писать бэкенд на Dart
Если вы работаете с Flutter, вы знаете Dart. Вы понимаете async/await, работаете с моделями и репозиториями, привыкли к чистой архитектуре. Вы запускали приложения на реальных устройствах. Между этим и умением написать и запустить рабочий бэкенд - пропасть меньше, чем кажется. Не нужно учить новый язык. Нужно понять, как Dart работает, когда нет виджетов, нет BuildContext, нет Flutter. Есть только процесс, который принимает HTTP-запросы, ходит в базу данных и отправляет ответы.
Автор показывает этот путь на примере API для управления пользователями и профилями. Все на знакомом Dart и фреймворке Shelf. Проект поднимается в Docker с PostgreSQL, проверяет пользователей через JWT-токены и деплоится на Fly.io.
Как Dart работает на сервере:
В Flutter-приложении ваш код работает поверх огромного количества логики: дерево виджетов, пайплайн рендеринга, управление состоянием, обработка событий платформы. На сервере ничего этого нет.
Есть процесс, который слушает порт, получает HTTP-запросы, делает работу и отправляет ответы. В стандартной библиотеке Dart есть все необходимое для этого на самом низком уровне.
import 'dart:io';
void main() async {
final server = await HttpServer.bind('0.0.0.0', 8080);
print('Сервер запущен на порту 8080');
await for (final request in server) {
request.response
..statusCode = 200
..write('Привет из Dart')
..close();
}
}
Это рабочий HTTP-сервер. Никаких пакетов, никаких фреймворков. Каждый запрос приходит через HttpServer, и вы пишете ответ напрямую.
Но как только появляются маршруты, middleware, аутентификация и обработка ошибок, работать с dart:io становится неудобно. Здесь в игру вступает Shelf.
Что такое Shelf:
Shelf - это библиотека для создания веб-серверов. Она не пытается быть полноценным фреймворком. Вместо этого она дает базовые кирпичики, из которых вы собираете именно то, что нужно.
В Shelf есть четыре ключевых понятия:
Handler - функция, которая принимает Request и возвращает Response. Все в Shelf в итоге сводится к хендлеру.
Middleware - функция, которая оборачивает хендлер, добавляя поведение до или после его выполнения. Логирование, аутентификация и обработка ошибок - это middleware.
Pipeline - цепочка middleware с хендлером в конце. Запрос проходит через все middleware, прежде чем добраться до хендлера.
Router - сопоставляет URL-паттерны и HTTP-методы с конкретными хендлерами.
Если вы использовали навигацию в Flutter или что-то похожее на провайдеры, модель композиции будет понятна. Маленькие, независимые части собираются в работающее целое.
Структура проекта:
Автор статьи предлагает структуру, которая будет знакома любому Flutter-разработчику:
user_profile_api/
bin/
server.dart - точка входа
lib/
config/ - настройки и подключение к БД
handlers/ - хендлеры для эндпоинтов
middleware/ - логирование, ошибки, аутентификация
models/ - модели данных
repositories/ - работа с базой данных
services/ - бизнес-логика (JWT, хеширование)
router.dart - маршрутизация
migrations/ - SQL-скрипты
docker-compose.yml
Dockerfile
.env
Модели, репозитории, сервисы - это те же понятия, что и в Flutter-проекте. Хендлеры заменяют ViewModel или контроллеры. Middleware - перехватчики.
База данных и миграции:
В проекте используется PostgreSQL. Все поднимается через Docker Compose. Миграции применяются автоматически при старте приложения.
Структура базы данных простая: таблица users и таблица profiles, связанная один к одному. Код написан так, что разработчику не нужно писать сырые SQL-запросы в хендлерах - вся работа с базой инкапсулирована в репозиториях.
Аутентификация и защита:
Автор использует JWT-токены. Пароли хешируются с помощью bcrypt. Регистрация и логин - публичные эндпоинты. Все остальное защищено middleware, который проверяет наличие и валидность токена перед тем, как пустить запрос дальше.
Особенно хорошо сделан один момент: при неудачной попытке входа сервер возвращает общее сообщение «Неверный email или пароль», а не уточняет, что именно не так. Это защита от перебора пользователей.
Обработка ошибок:
Вместо того чтобы в каждом хендлере писать try/catch, автор выносит обработку ошибок в отдельное middleware. Это гарантирует единый формат ответа для всех ошибок и, что важно, скрывает от клиента внутренние детали. Пользователь никогда не увидит стектрейс или детали ошибки базы данных.
Деплой:
Автор статьи показывает два пути. Локально - через Docker Compose, где приложение и база данных поднимаются вместе. Продакшен - на Fly.io, где управляемый PostgreSQL и автоматический TLS настроены практически из коробки.
Вывод:
Эта статья - хороший пример того, что знание Dart не заканчивается на Flutter. Те же модели, репозитории, асинхронность и архитектурные подходы работают и на сервере. Нужно только сменить контекст: вместо виджетов - обработка запросов, вместо State - база данных.
Shelf не делает за вас выборов. Он дает кирпичики, а архитектуру вы собираете сами. Это философски близко к Flutter, где вы тоже строите UI из базовых компонентов.
Если вы знаете Dart, бэкенд уже не выглядит чем-то недоступным.