Демо API бронирования мест:
- один пользователь — одна бронь на одно событие,
- без овербукинга (конкурентно-безопасно, блокировка
FOR UPDATE+ уникальный индекс).
Base URL: https://booking-api-mwff.onrender.com
Быстрые ссылки:
- Домашняя страница —
/ - Документация (Swagger UI) —
/docs - Health-check (app) —
/health - Health-check (DB) —
/health/db - Основной эндпоинт —
POST /api/bookings/reserve
Откройте в браузере:
/health→ ожидается:{"status":"ok","db":"configured"}/health/db→ ожидается:{"status":"ok"}(при живой БД) или503 {"status":"db-down"}/docs→ откроется Swagger UI (можно тестировать API из браузера)
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"user123"}'Ожидается:
- статус
HTTP/1.1 201 - JSON с полями
bookingиseats_left.
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"user123"}'Ожидается:
- статус
HTTP/1.1 409 - JSON
{"error":"User already booked this event"}.
# три уникальные брони (должны пройти: 201 201 201)
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"u2"}'
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"u3"}'
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"u4"}'
# четвёртая попытка (ожидаем 409 sold out)
curl -i -X POST https://booking-api-mwff.onrender.com/api/bookings/reserve \
-H "Content-Type: application/json" \
-d '{"event_id":1,"user_id":"u5"}'Ожидается:
- первые три запроса вернут
201, - четвёртый —
409и сообщениеEvent is sold out.
Отправьте несколько запросов одновременно (через Swagger UI или скриптом) на одно и то же event_id с разными user_id.
Итог: число успешных бронирований не превысит total_seats, а лишние вернут 409.
-
POST /api/bookings/reserve-
Вход: JSON
{"event_id": number, "user_id": string}event_id: целое> 0user_id: непустая строка, ≤ 128 символов, допускаются толькоA–Z a–z 0–9 _ - . : @
-
Выход:
201 Created— бронь создана,{ booking, seats_left }400 Bad Request— неверный формат входа404 Not Found— событие не найдено409 Conflict— повторная бронь того же пользователя или мест больше нет503 Service Unavailable— БД не настроена (DATABASE_URLне задан)500 Internal Error— непредвиденная ошибка
-
-
Ограничение частоты: на
POST /api/bookings/reserveдействует rate-limit 30 запросов/мин на IP.
helmet(HTTP-заголовки безопасности, настроенcrossOriginResourcePolicyдля Swagger UI)cors(разрешены кросс-доменные запросы)express.json({ limit: '32kb' })(лимит тела запроса)morgan('combined')(логирование запросов)app.set('trust proxy', 1)(корректно определяем IP клиента за прокси/балансировщиком — важно для rate-limit)- Graceful shutdown по
SIGINT/SIGTERM(корректное закрытие соединений с БД) - Fallback 404 JSON-ответом
Требования: Node.js 18+; доступная PostgreSQL (или облачная строка подключения).
-
Установите зависимости:
npm ci
-
Создайте
.envна основе.env.exampleи задайте переменные:PORT=3000 DATABASE_URL=postgres://user:password@host:5432/dbname # Для облачных БД (Neon/Render) включите SSL: DATABASE_SSL=true
-
Запуск:
npm start
-
Проверьте:
GET http://localhost:3000/healthGET http://localhost:3000/docs
Выполните файл db/migrations.sql в вашей БД (создаёт таблицы, индексы и сид-данные).
Пример через psql:
psql "postgresql://USER:[email protected]:PORT/DB?sslmode=require" -f db/migrations.sql.
├─ db/
│ └─ migrations.sql # таблицы, индексы, сиды
├─ __tests__/ # автотесты
├─ openapi.yaml # спецификация OpenAPI для Swagger UI
├─ server.js # запуск приложения, graceful shutdown
├─ package.json
├─ .env.example
└─ README.md
- CI: GitHub Actions (
.github/workflows/ci.yml) выполняетnpm ciиnpm testна каждыйpush/PR. - CD: Render Blueprint (
render.yaml) автодеплоит веткуmain;/healthиспользуется как health-check.