У мини-приложений Telegram долго не было приличного способа хранить данные на устройстве. Встроенный CloudStorage живёт в облаке: 1024 ключа, до 4096 символов на значение, общий между всеми телефонами пользователя. Настройки — нормально. Токены — рискованно. Кэш — медленно.
11 апреля 2025 года вместе с Bot API 9.0 пришли два новых хранилища. DeviceStorage — постоянное, прямо на устройстве. SecureStorage — зашифрованное, для чувствительных данных. Ни одно из них не уходит в облако.
Что это и как начать
Представьте localStorage в браузере — знакомый интерфейс setItem, getItem, removeItem, данные переживают перезагрузку страницы. DeviceStorage устроен похожим образом, но с двумя отличиями: данные живут внутри Telegram-клиента, а не в браузере, и изолированы по ботам. Ваше мини-приложение не может прочитать данные, которые записало другое.
SecureStorage — отдельный слой для чувствительных данных. На iOS он использует Keychain — системное хранилище, в котором лежат пароли от приложений, сертификаты и ключи. На Android — Keystore, аналогичный механизм. Шифрование берёт на себя операционная система; мини-приложение работает с открытым текстом, а систему заботит, как его спрятать.
Оба хранилища локальные. Если пользователь откроет мини-приложение с другого телефона — данные будут пустыми. Для синхронизации между устройствами остаётся CloudStorage или собственный бэкенд.
Три шага для старта:
- Подключите официальный скрипт telegram-web-app.js в HTML
- Проверьте, что клиент поддерживает Bot API 9.0 через tg.isVersionAtLeast(“9.0”)
- Обращайтесь к tg.DeviceStorage и tg.SecureStorage — методы те же, что у localStorage
Пять минут на подключение, не преувеличение.
Как устроено
DeviceStorage — локальный слой
DeviceStorage — постоянное хранилище на устройстве. До 5 МБ на одного пользователя для одного мини-приложения. Данные не уходят в облако и не синхронизируются между устройствами. Если пользователь сменит телефон — хранилище будет пустым.
| Метод | Что делает |
|---|---|
| setItem(key, value, [callback]) | Записать значение по ключу |
| getItem(key, callback) | Прочитать значение |
| removeItem(key, [callback]) | Удалить ключ |
| clear([callback]) | Очистить всё хранилище бота на устройстве |
Все методы возвращают объект DeviceStorage — их можно вызывать цепочкой. Колбэк необязательный, но без него вы не узнаете об ошибке.
SecureStorage — защищённый слой
SecureStorage — для данных, которые не должны лежать в открытом виде. До 10 элементов на пользователя. На iOS шифрование обеспечивает Keychain, на Android — Keystore. Значения шифруются на уровне ОС: другие приложения физически не могут их прочитать.
| Метод | Что делает |
|---|---|
| setItem(key, value, [callback]) | Сохранить секрет |
| getItem(key, callback) | Прочитать секрет. Третий аргумент — canRestore |
| restoreItem(key, [callback]) | Попросить пользователя восстановить ключ через системный диалог |
| removeItem(key, [callback]) | Удалить |
| clear([callback]) | Стереть всё |
Главное отличие от DeviceStorage — метод restoreItem. Если ключ удалён, но операционная система может его восстановить (например, через iCloud Keychain Sync), getItem вернёт null и флаг canRestore: true. Вызов restoreItem покажет пользователю системный диалог — восстановить или нет.
Три хранилища рядом
К моменту выхода Bot API 9.0 у Mini Apps было три хранилища, каждое со своей задачей:
| Хранилище | Где хранит | Лимит | С версии | Назначение |
|---|---|---|---|---|
| CloudStorage | Облако | 1024 ключа, 4096 символов | Bot API 6.9 | Кросс-девайс данные |
| DeviceStorage | Устройство | 5 МБ | Bot API 9.0 | Кэш, UI-состояние |
| SecureStorage | Устройство, зашифровано | 10 элементов | Bot API 9.0 | Токены, секреты |
CloudStorage синхронизирует между устройствами. DeviceStorage и SecureStorage — нет. Выбор хранилища — это вопрос не скорости, а архитектуры: где данные должны жить и кто их должен видеть.
Инсайт: DeviceStorage берёт количеством — 5 МБ для кэша, настроек, состояния. SecureStorage берёт качеством — 10 слотов, но каждый под защитой ОС. Пытаться запихнуть интерфейсный стейт в защищённое хранилище — как хранить обувь в сейфе: место дорого, а пользы ноль.
Подключение за четыре шага
Шаг 1. Подключить Telegram Web App SDK
Подключите официальный скрипт в разметку страницы:
<script src="https://telegram.org/js/telegram-web-app.js"></script>
После загрузки скрипта оба хранилища появятся как поля объекта window.Telegram.WebApp.
Шаг 2. Проверить версию Bot API
Эти хранилища появились в Bot API 9.0 — клиенты младше апреля 2025 года их не знают. Перед обращением проверяйте версию и наличие объекта:
const tg = window.Telegram.WebApp;
if (tg.isVersionAtLeast("9.0") && tg.DeviceStorage) {
// безопасно использовать DeviceStorage
} else {
// фолбэк на localStorage или CloudStorage
}
Шаг 3. Записать и прочитать значение
Интерфейс DeviceStorage повторяет привычный localStorage:
const storage = window.Telegram.WebApp.DeviceStorage;
storage.setItem("ui:theme", "dark", (err, ok) => {
if (err) return console.error(err);
console.log("Сохранено:", ok);
});
storage.getItem("ui:theme", (err, value) => {
if (err) return console.error(err);
console.log("Тема:", value); // "dark" или null
});
Шаг 4. Хранить токены через SecureStorage
Логика сложнее из-за механизма восстановления. Когда токен удалён, но ОС может его вернуть — getItem сообщит об этом флагом canRestore:
const secure = window.Telegram.WebApp.SecureStorage;
secure.setItem("auth:refresh_token", refreshToken, (err, ok) => {
if (err) return console.error("Не удалось сохранить токен", err);
});
secure.getItem("auth:refresh_token", (err, value, canRestore) => {
if (err) return;
if (value) {
bootstrap(value);
} else if (canRestore) {
secure.restoreItem("auth:refresh_token", (err, restored) => {
if (restored) bootstrap(restored);
});
} else {
redirectToLogin();
}
});
Совет: используйте единый префикс для ключей — app: или feature:. Данные и так изолированы между ботами, но префикс помогает внутри одного приложения: легче мигрировать и точечно очищать отдельные подсистемы через clear().
Когда использовать
- Кэш и UI-состояние — тема оформления, язык, последний раздел, свёрнутые группы. При повторном открытии пользователь сразу видит свой интерфейс, а не дефолтный — без запроса к серверу.
- Офлайн-кэш — прайсы, списки заказов, FAQ сохраняются локально и подгружаются на холодном старте, пока идёт сетевой запрос. Интерфейс отрисовывается мгновенно, данные подтягиваются потом.
- Токены авторизации — защищённое хранилище заменяет ненадёжные схемы с хранением токенов в открытом localStorage или облачном CloudStorage. Refresh-токен от собственного бэкенда, работающего поверх initData, логично класть именно сюда.
- Биометрический вход — в комбинации с BiometricManager, доступным с Bot API 7.2, защищённое хранилище реализует привычный паттерн: первый вход по паролю, токен уходит в SecureStorage, дальше разблокировка по Face ID или отпечатку пальца.
- A/B тесты на устройстве — бакет пользователя фиксируется в локальном хранилище, чтобы при каждом запуске он оставался в своей группе эксперимента.
- Перевод с CloudStorage — если в облаке лежало всё подряд, стоит разложить по местам: настройки и кэш — локально, кросс-девайс данные — в облаке, секреты — в защищённом хранилище.
Ограничения
Ограничения
Только Bot API 9.0+ — Хранилища недоступны на старых клиентах.
Telegram-клиенты до апреля 2025 года не поддерживают DeviceStorage и SecureStorage. Всегда делайте feature-detection через tg.isVersionAtLeast(“9.0”) и предусматривайте фолбэк на localStorage или CloudStorage. Без проверки обращение к несуществующему объекту — TypeError и сломанное мини-приложение.
Нет синхронизации между устройствами — Оба хранилища работают на конкретном устройстве.
При открытии мини-приложения с другого телефона данные будут пусты. Для данных, которые нужны везде, подойдёт CloudStorage: до 1024 ключей, значения до 4096 символов, ключи только из латиницы, цифр, подчёркивания и дефиса. Альтернатива — собственный бэкенд.
SecureStorage — 10 элементов максимум — Лимит жёсткий и документирован в официальной спецификации.
SecureStorage не рассчитан на хранение массивов данных; каждый элемент — отдельный секрет. Если нужно больше — это сигнал, что вы используете не тот инструмент.
DeviceStorage — 5 МБ на пользователя — Объём щедрый для интерфейсных данных, но не безграничный.
Кэш списков, прайсов и настроек поместится. Медиафайлы и бинарные данные — нет; для них нужен собственный бэкенд или CDN, не локальное хранилище.
Антипаттерны
Антипаттерны
Хранить токены в CloudStorage — Облако синхронизируется между устройствами, но данные лежат в открытом виде.
Токен авторизации в CloudStorage — это утечка, которая рано или поздно случится. Для токенов существует SecureStorage с шифрованием на уровне ОС.
Складывать весь стейт в SecureStorage — 10 элементов на всю жизнь мини-приложения.
Если вы пытаетесь запихнуть туда UI-состояние, кэш или списки — вы используете защищённое хранилище не по назначению. SecureStorage — для секретов, не для данных. Обычные данные — в DeviceStorage.
Не проверять версию клиента — Если клиент старше Bot API 9.0, объекты DeviceStorage и SecureStorage просто не существуют.
Обращение к ним без проверки — TypeError и неработающее мини-приложение. Feature-detection через isVersionAtLeast занимает одну строку и спасает весь сценарий.
Игнорировать canRestore — На iOS Keychain может восстанавливать удалённые ключи через iCloud Sync.
Если ваш код не обрабатывает canRestore: true в колбэке getItem, пользователь после переустановки приложения потеряет доступ к токену, хотя система может его вернуть через restoreItem.
Считать, что SecureStorage делает приложение безопасным — Это лишь правильное место хранения.
Логику защиты — ротация refresh-токенов, проверка initData на сервере, ограничение скоупа ключей — вы пишете сами. Хранилище не заменяет архитектуру безопасности.
Чеклист
Чеклист
Проверка версии клиента — Перед обращением к DeviceStorage и SecureStorage вызываете tg.isVersionAtLeast(“9.0”).
Для старых клиентов предусмотрен фолбэк на localStorage или CloudStorage.
Разделение данных по хранилищам — Токены и секреты — в SecureStorage.
UI-состояние и кэш — в DeviceStorage. Кросс-девайс данные — в CloudStorage или на собственном бэкенде. Ничего не смешано.
Единый префикс ключей
— Ключи названы с единым префиксом (app:, feature:) для удобной миграции и точечного clear() отдельных подсистем.
Обработка canRestore — В колбэке getItem для SecureStorage обрабатываете флаг canRestore.
Если true — вызываете restoreItem и показываете пользователю системный диалог.
Верификация initData на сервере — Не доверяете клиентскому токену без проверки.
initData проверяется на сервере, refresh-токен от бэкенда хранится в SecureStorage, ротация настроена.
Ссылки
Ссылки
- Документация: DeviceStorage
- Документация: SecureStorage
- Документация: CloudStorage
- Документация: Bot API 9.0 changelog
- Документация: @BotNews
- Документация: grammY
- Документация: @telegram-apps/sdk