У мини-приложений 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 в Telegram Mini Apps

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().

Кодовый пример SecureStorage с проверкой Bot API 9.0, getItem, canRestore и restoreItem

Когда использовать

  • Кэш и 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, ротация настроена.

Ссылки

Ссылки