Upsert паттерн
API использует upsert-паттерн: один PUT запрос создаёт новый объект или обновляет существующий. Вам не нужно проверять, есть ли объект в системе — API делает это автоматически.
Зачем это нужно
В классическом REST API для синхронизации данных нужно:
1. GET /listings/external/APT-001 # Проверить существование
2. Если 404 → POST /listings # Создать
Если 200 → PUT /listings/42 # Обновить
С upsert-паттерном всё проще:
1. PUT /listings { externalId: "APT-001", ... }
# API сам создаст или обновит
Как работает
| Ситуация | Что происходит |
|---|---|
| Объект не найден | Создаётся новый |
| Объект найден | Обновляется существующий |
Поиск происходит по id или externalId — что передали в запросе.
Пример: создание
Первый запрос с externalId: "APT-001" создаёт объявление:
curl -X PUT https://crm.rentix.md/api/v1/listings \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "APT-001",
"announcementType": "rent",
"propertyType": "residential",
"propertySecondaryType": "apartment",
"announcementValue": 500,
"announcementCurrency": "EUR"
}'
{
"id": 42,
"externalId": "APT-001",
"status": "draft",
"created": true,
"updated": false
}
Обратите внимание: created: true — объект создан.
Пример: обновление
Повторный запрос с тем же externalId обновляет объявление:
curl -X PUT https://crm.rentix.md/api/v1/listings \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "APT-001",
"announcementValue": 600
}'
{
"id": 42,
"externalId": "APT-001",
"status": "draft",
"created": false,
"updated": true
}
Теперь updated: true — объект обновлён.
Частичное обновление
При обновлении передавайте только изменённые поля. Остальные сохранят свои значения:
// Первый запрос — полное создание
{
"externalId": "APT-001",
"announcementType": "rent",
"announcementValue": 500,
"propertyArea": 65,
"propertyFloorNumber": 3
}
// Второй запрос — меняем только цену
{
"externalId": "APT-001",
"announcementValue": 600
}
// propertyArea и propertyFloorNumber останутся прежними
Очистка полей
Чтобы очистить поле (удалить значение), отправьте null:
{
"externalId": "APT-001",
"propertyFloorNumber": null
}
После этого запроса propertyFloorNumber будет пустым.
Преимущества
Простота интеграции
Один endpoint на все случаи. Не нужно писать логику проверки существования.
Идемпотентность
Повторный запрос с теми же данными не создаст дубликат — просто обновит существующий объект. Это важно при сбоях сети: можно безопасно повторить запрос.
Атомарность
Создание или обновление происходит в одной транзакции. Не бывает ситуации «проверил — не было, создал — дубликат».
Bulk upsert
Тот же принцип работает для массовых операций. До 100 объектов за запрос. Каждая операция должна содержать поле op:
curl -X POST https://crm.rentix.md/api/v1/listings/bulk \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{ "op": "upsert", "externalId": "APT-001", "announcementValue": 500 },
{ "op": "upsert", "externalId": "APT-002", "announcementValue": 700 },
{ "op": "upsert", "externalId": "APT-003", "announcementValue": 450 }
]
}'
{
"results": [
{ "op": "upsert", "externalId": "APT-001", "id": 42, "success": true, "created": false, "updated": true },
{ "op": "upsert", "externalId": "APT-002", "id": 43, "success": true, "created": true, "updated": false },
{ "op": "upsert", "externalId": "APT-003", "id": 44, "success": true, "created": true, "updated": false }
],
"summary": { "total": 3, "succeeded": 3, "failed": 0 }
}
Ограничения
id и externalId одновременно — будет ошибка. Выберите один способ идентификации.| Ресурс | Endpoint |
|---|---|
| Объявления | PUT /listings |
| Пользователи | PUT /users |