REST API для управления задачами с поддержкой двусторонней интеграции с внешними системами на примере Service Desk.
- Node.js 24
- TypeScript
- Express
- Prisma
- PostgreSQL 17
- Docker / Docker Compose
- Docker
- Docker Compose
Создать .env на основе .env.example.
Пример:
DATABASE_URL="postgresql://taskhub:taskhub@localhost:334/taskhub?schema=public"
SERVICE_DESK_INTEGRATION_TOKEN="replace-with-service-desk-token"
SERVICE_DESK_BASE_URL="https://service-desk.example/api"SERVICE_DESK_BASE_URL должен содержать адрес REST API внешнего Service
Desk. Если внешний Service Desk отсутствует, основное API Task Hub
продолжает работать независимо от него.
docker compose up --buildПосле запуска API доступен по адресу:
http://localhost:333
PostgreSQL доступен на локальном порту:
localhost:334
docker compose downДанные PostgreSQL сохраняются в Docker volume и не удаляются при обычном
docker compose down.
- Node.js 24+
- PostgreSQL
Установить зависимости:
npm ciСоздать .env на основе .env.example.
Применить миграции:
npx prisma migrate deployСгенерировать Prisma Client:
npx prisma generateЗапустить приложение:
npm run devПроверка типов:
npm run typecheckСборка:
npm run buildОсновные endpoints:
POST /tasks
GET /tasks
GET /tasks/:id
PATCH /tasks/:id
PUT /tasks/:id
DELETE /tasks/:id
POST /tasks
Обязательные поля:
titlepriority
status по умолчанию имеет значение todo.
Допустимые статусы:
todoin_progressdone
Допустимые приоритеты:
lowmediumhigh
due_date передаётся в формате ISO date-time с обязательным timezone.
Например:
{
"title": "Подготовить отчёт",
"priority": "high",
"due_date": "2026-08-03T18:00:00+03:00"
}При создании задачи due_date не может находиться в прошлом.
Успешный запрос возвращает 201 Created и идентификатор созданной
задачи:
{
"id": "5da25b33-fb3d-4402-915a-ad1f02eeee03"
}GET /tasks
Поддерживаются параметры:
offsetlimitstatussort=due_date|priorityorder=asc|desc
Пагинация, фильтрация и сортировка могут использоваться одновременно.
Пример:
GET /tasks?offset=0&limit=10&status=todo&sort=priority&order=descPATCH /tasks/:id выполняет частичное изменение задачи --- обновляются
только переданные поля.
PUT /tasks/:id полностью заменяет изменяемые данные задачи.
Для PUT обязательны:
titlestatuspriority
Если due_date при PUT отсутствует, значение устанавливается в
null.
При изменении существующей задачи через PATCH или PUT допускается
установка due_date в прошлом.
DELETE /tasks/:id реализован как soft delete.
Запись физически остаётся в PostgreSQL, а поле deleted_at получает
время удаления.
Удалённые задачи:
- не возвращаются в
GET /tasks; - возвращают
404 Not Foundпри запросе по ID.
Основные используемые коды:
Код Значение
200 успешное получение или изменение
201 задача создана
204 задача удалена
400 некорректный формат входных данных
401 ошибка авторизации интеграции
404 задача не найдена
409 конфликт состояния интеграции
422 нарушение бизнес-правила
500 внутренняя ошибка сервера
Некорректный формат данных возвращает 400 Bad Request.
Например:
- неизвестный
status; - неизвестный
priority; - некорректный UUID;
- некорректный формат
due_date; - отсутствие timezone в
due_date.
Корректно сформированный запрос, нарушающий бизнес-правило создания
задачи с датой исполнения в прошлом, возвращает
422 Unprocessable Entity.
Реализована двусторонняя интеграция Task Hub с внешним Service Desk.
Внешняя система отправляет события через:
POST /integrations/service-desk/webhookWebhook защищён Bearer-токеном:
Authorization: Bearer <SERVICE_DESK_INTEGRATION_TOKEN>Пример события:
{
"event": "ticket.created",
"ticket": {
"id": "SD-14852",
"subject": "Не работает выгрузка отчёта",
"state": "new",
"urgency": 1,
"due_date": "2026-08-03T18:00:00+03:00"
}
}Данные Service Desk преобразуются в модель Task Hub.
Соответствие статусов:
Service Desk Task Hub
new todo
in_progress in_progress
resolved done
closed done
Соответствие приоритетов:
Service Desk Task Hub
1 high
2 medium
3 low
При первом событии для внешнего тикета создаётся новая задача.
Связь между задачей Task Hub и объектом внешней системы сохраняется в
IntegrationBinding.
Повторное событие с тем же идентификатором внешнего объекта обновляет уже существующую задачу вместо создания дубликата.
Если задача связана с тикетом Service Desk через IntegrationBinding,
изменение задачи через:
PATCH /tasks/:id
PUT /tasks/:id
инициирует исходящий HTTP-запрос во внешний Service Desk:
PATCH {SERVICE_DESK_BASE_URL}/tickets/{external_entity_id}Task Hub преобразует свою модель обратно в модель Service Desk.
Соответствие статусов:
Task Hub Service Desk
todo new
in_progress in_progress
done resolved
Соответствие приоритетов:
Task Hub Service Desk
high 1
medium 2
low 3
Для авторизации исходящего запроса используется Bearer-токен.
Если внешний Service Desk временно недоступен, изменение задачи в Task Hub сохраняется, а ошибка синхронизации записывается в лог.
Входящий webhook обновляет задачу непосредственно через integration-модуль и не запускает обратную синхронизацию в тот же Service Desk. Это предотвращает цикл повторных изменений между системами.
Описание API находится в:
openapi.json
Спецификация содержит описание endpoints, параметров, моделей запросов и ответов, а также HTTP-кодов.
Готовая коллекция находится в:
postman/TaskHub.postman_collection.json
Коллекция содержит последовательный сценарий проверки API, включая:
- создание задачи;
- валидацию;
- пагинацию, фильтрацию и сортировку;
- получение задачи;
PATCH;PUT;- soft delete;
- проверку удалённой задачи;
- входящее создание задачи через Service Desk webhook;
- обновление задачи повторным событием Service Desk;
- проверку авторизации webhook.
При создании задачи её id автоматически сохраняется в переменную
taskId и используется последующими запросами.
src/
├── index.ts
├── db.ts
├── task/
│ ├── task.route.ts
│ └── task.service.ts
└── integration/
├── integration.service.ts
└── service-desk/
├── service-desk.route.ts
├── service-desk.service.ts
├── service-desk.mapper.ts
└── service-desk.client.ts
task.route.ts отвечает за HTTP-слой и проверку формата входных данных.
task.service.ts содержит операции над задачами и работу с Prisma.
integration.service.ts является общей точкой запуска синхронизации с
внешними системами.
Модуль service-desk разделён на:
service-desk.route.ts--- входящий webhook;service-desk.service.ts--- сценарии синхронизации и работа со связями;service-desk.mapper.ts--- преобразование моделей Task Hub и Service Desk;service-desk.client.ts--- исходящие HTTP-запросы в Service Desk.
Связь внутренних задач с объектами внешних систем хранится отдельно в
модели IntegrationBinding, поэтому модель Task не зависит от
конкретной внешней системы.