Telegram CloudStorage — это key-value хранилище в API Telegram Mini Apps, привязанное к паре «бот + пользователь». Данные хранятся на стороне Telegram, синхронизируются между всеми устройствами юзера и доступны без сервера. Для маленьких настроек, состояния UI и кешей это идеальная замена localStorage и IndexedDB.
Разберём API, лимиты, паттерны использования, как сочетать CloudStorage с серверным API и какие сценарии она закрывает без бэкенда.
Что такое CloudStorage
CloudStorage появился в Bot API 6.9 (2023). Доступен через window.Telegram.WebApp.CloudStorage — асинхронный key-value API:
const cs = window.Telegram.WebApp.CloudStorage;
cs.setItem("theme", "dark", (err, ok) => { /* ... */ });
cs.getItem("theme", (err, value) => console.log(value));
cs.removeItem("theme", (err) => { /* ... */ });
cs.getKeys((err, keys) => console.log(keys));
cs.getItems(["theme", "lang"], (err, dict) => console.log(dict));
cs.removeItems(["theme", "lang"], (err) => { /* ... */ });
API колбэк-стиль. В современных приложениях оборачивают в Promise:
const setItem = (k: string, v: string) =>
new Promise<boolean>((resolve, reject) =>
cs.setItem(k, v, (err, ok) => (err ? reject(err) : resolve(ok)))
);
Лимиты
| Параметр | Лимит |
|---|---|
| Максимум ключей | 1024 на (бот, юзер) |
| Длина ключа | 1–128 символов, [A-Za-z0-9_-] |
| Длина значения | 0–4096 символов |
| Общий размер | ~4 МБ суммарно |
| Скорость записи | ~10 операций/сек (мягкий лимит) |
Это не БД: 4 МБ — потолок. Для каталогов, сообщений и больших объектов CloudStorage не подходит.
Что туда класть
Хорошие кейсы:
- UI-состояние: тема, язык, выбранная вкладка, последний поиск.
- Корзина в маленьком магазине: до 50 товаров.
- Настройки уведомлений: что и когда присылать.
- Прогресс прохождения: какой урок открыт, какой бейдж получен.
- Кеш профиля: чтобы Mini App открывался мгновенно без запроса к бэкенду.
- Черновики форм.
Плохие кейсы — JWT-токены (они должны быть в HttpOnly cookie или памяти), пароли, личные данные клиентов в b2b-сценариях.
Паттерн: «офлайн-первый» Mini App
Mini App должен открываться мгновенно даже на медленном интернете. Паттерн:
async function bootstrap() {
// 1. Поднимаем UI из CloudStorage сразу
const cached = await getItem("user_profile_v1");
if (cached) {
renderApp(JSON.parse(cached));
} else {
renderSkeleton();
}
// 2. Параллельно подтягиваем свежие данные
try {
const fresh = await api.getProfile();
await setItem("user_profile_v1", JSON.stringify(fresh));
renderApp(fresh);
} catch {
/* остаёмся на кешированном */
}
}
Юзер видит экран за 50 мс вместо 500 мс «спиннер → загрузка». TTI Mini App падает в 5–10 раз.
Синхронизация между устройствами
CloudStorage синхронизируется автоматически между всеми клиентами Telegram юзера. Если на iPhone положили cart_v1, через 1–3 секунды она появится на Mac и Android.
Это даёт «бесплатный» continuity: юзер начал заказ на телефоне, продолжил на компьютере, корзина та же.
Версионирование схемы
CloudStorage не миграбелен: данные старого формата останутся как есть. Поэтому версионируйте ключи:
const KEY = "cart_v2"; // v1 был с другой схемой
async function getCart() {
const raw = await getItem(KEY);
if (raw) return JSON.parse(raw);
// мигрируем со старой версии один раз
const old = await getItem("cart_v1");
if (old) {
const migrated = migrate(JSON.parse(old));
await setItem(KEY, JSON.stringify(migrated));
await removeItem("cart_v1");
return migrated;
}
return defaultCart();
}
Сжатие больших значений
4096 символов на значение — мало для JSON-объекта с 50 полями. Решение — JSON + LZ-string:
import LZString from "lz-string";
const setBig = (k: string, obj: any) =>
setItem(k, LZString.compressToBase64(JSON.stringify(obj)));
const getBig = async (k: string) => {
const raw = await getItem(k);
return raw ? JSON.parse(LZString.decompressFromBase64(raw)!) : null;
};
Сжатие даёт 3–5х экономию места на типичном JSON.
Сравнение с альтернативами
| Хранилище | Объём | Кросс-устройство | Жизненный цикл |
|---|---|---|---|
| CloudStorage | ~4 МБ | да | пока бот не удалён |
| localStorage | ~5 МБ на домен | нет | пока юзер не очистит браузер |
| IndexedDB | ~50–100 МБ | нет | то же |
| Бэкенд (PostgreSQL) | сколько угодно | да | контролируете вы |
Для микро-настроек CloudStorage. Для пользовательских данных среднего объёма — бэкенд. Для больших офлайн-кешей — IndexedDB на стороне Mini App.
Гонки и идемпотентность
Если юзер на двух устройствах одновременно меняет один ключ, побеждает последний setItem — без conflict resolution. Поэтому критичные данные пишите через бэкенд, а CloudStorage — для локального состояния, где гонка некритична.
Безопасность
CloudStorage доступен только конкретной паре (бот, юзер). Другие боты, другие юзеры этих данных не видят. Telegram шифрует их в транспорте и хранит на своих серверах.
Тем не менее, не кладите туда:
- Платёжные карты, CVV.
- Пароли в plain text.
- Чужие данные (адреса, телефоны других юзеров).
- Бизнес-секреты компании.
Топ-5 ошибок
- Кладут в CloudStorage большие массивы (>4 КБ) —
setItemтихо обрезает или падает. - Не оборачивают callback API в Promise — лапша колбэков по всему коду.
- Используют как первичное хранилище без бэкапа в БД — теряют данные при удалении бота юзером.
- Пишут JWT в CloudStorage — он становится exfiltrable XSS-атакой.
- Не версионируют ключи — старая схема сосуществует с новой и ломает приложение.
Итого
CloudStorage — лучший friend Mini App-разработчика для локального состояния, кешей и пользовательских настроек. Лимит 4 МБ суммарно, синхронизация между устройствами «из коробки», нулевой бэкенд-оверхед. Используйте как кеш и UI-state, не как primary storage; версионируйте ключи; сжимайте большие значения; и не кладите туда секреты — это всё, что нужно знать.
Частые вопросы
Что происходит с данными, если юзер удалил бота?
Telegram гарантированно удаляет всё CloudStorage этой пары (bot, user) в течение 30 дней. Если юзер вернётся и снова напишет /start, он получит чистый CloudStorage. Поэтому критичные данные дублируйте на бэкенд, привязывая к telegram_id.
Можно ли читать CloudStorage с бэкенда?
Нет. CloudStorage доступен только из Mini App в контексте конкретного юзера. С бэкенда вы можете только сказать Mini App «отправь мне свои данные через sendData», и Mini App сам выгрузит CloudStorage и отправит. Это by design: данные принадлежат юзеру.
Как обновить значение атомарно?
Никак — CloudStorage не имеет CAS (compare-and-swap). Если нужно атомарное обновление счётчика или списка, держите его на бэкенде, а CloudStorage используйте только как кеш для UI. Альтернатива — versioning внутри значения и retry-цикл при конфликте.
Сколько раз в секунду можно вызывать setItem?
Документированного лимита нет, но на практике >10 операций/сек начинают давать ошибки или задержки. Дебаунсите запись: накапливаете изменения 200–500 мс и пишете батчем через setItems.
Работает ли CloudStorage в desktop-клиентах?
Да, начиная с Telegram Desktop 4.10+. На старых десктопах API возвращает unavailable, и Mini App должен это обработать (fallback на localStorage или бэкенд). Проверка — Telegram.WebApp.isVersionAtLeast("6.9").
Можно ли шифровать значения дополнительно?
Можно, но осмысленно только для специфичных b2b-кейсов. Ключ шифрования всё равно либо хранится в CloudStorage (бесполезно), либо запрашивается у бэкенда (тогда проще класть зашифрованное значение сразу на бэкенд). В 99% случаев нативной защиты Telegram достаточно.
Как мигрировать с localStorage на CloudStorage?
В первый запуск читаете localStorage, копируете в CloudStorage, очищаете localStorage. После этого работаете только с CloudStorage. Учтите, что localStorage был на одном устройстве, а CloudStorage синхронизируется — могут возникнуть конфликты, если юзер запустил Mini App параллельно на двух устройствах. Делайте merge по timestamp.