Skip to content

Storage Gets Type-Safe Keys, Scenes Learn onEnter & SQLite Joins the Family

January 1 – February 8, 2026

The headline of this cycle: @gramio/storage ships a major v2.0.0 with typed template literal keys, a brand-new SQLite adapter lands for Bun, and scenes get a long-awaited onEnter() hook. Plus: callback queries now expose chatId directly, and UniqueGiftInfo adapts to Telegram's new TON payment fields. Here's everything that shipped.

@gramio/storage v2.0.0 — Typed Keys with Template Literals

BREAKING: Storage interface now uses keyof Data for all keys

This is the biggest change of the cycle. The Storage<Data> interface has been completely reworked — all four methods (get, set, has, delete) now constrain keys to keyof Data and infer value types from the key automatically.

Before (v1.x):

ts
interface Storage<Data = any> {
    get<T = Data>(key: string): MaybePromise<Data | undefined>;
    set(key: string, value: Data): MaybePromise<void>;
    // ...
}

After (v2.0.0):

ts
interface Storage<Data = any> {
    get<K extends keyof Data>(key: K): MaybePromise<Data[K] | undefined>;
    set<K extends keyof Data>(key: K, value: Data[K]): MaybePromise<void>;
    has<K extends keyof Data>(key: K): MaybePromise<boolean>;
    delete<K extends keyof Data>(key: K): MaybePromise<boolean>;
}

This means TypeScript now infers the correct value type based on the key you're accessing — including template literal patterns:

ts
type Data = Record<`user:${number}`, { name: string; age: number }>
    & Record<`session:${string}`, { token: string; expires: number }>;

const storage = inMemoryStorage<Data>();

await storage.set("user:1", { name: "Alice", age: 30 });
const user = await storage.get("user:1");
//    ^? { name: string; age: number } | undefined

await storage.set("session:abc", { token: "xyz", expires: 123 });
const session = await storage.get("session:abc");
//    ^? { token: string; expires: number } | undefined

Migration: If you were using storage.get<SomeType>("key") to override the return type, this pattern no longer works. Instead, define your key-value mapping as a type parameter to the storage constructor.

Bug fix: inMemoryStorage generic types now flow correctly

The inMemoryStorage<Data>() function now properly passes its generic through to the Storage<Data> interface, fixing implicit any issues in previous versions.

@gramio/storage-sqlite — New SQLite Adapter (Bun)

A brand-new storage adapter backed by SQLite has landed! It uses bun:sqlite under the hood with WAL journal mode for maximum performance.

ts
import { sqliteStorage } from "@gramio/storage-sqlite";

// Pass a filename — creates a new database automatically
const storage = sqliteStorage({ filename: "bot-data.db" });

// Or pass an existing Bun SQLite Database instance
import { Database } from "bun:sqlite";
const db = new Database("bot-data.db");
const storage = sqliteStorage({ db });

Features:

  • TTL support — Set $ttl in seconds for automatic key expiry
  • Custom table name — Defaults to "gramio_storage", configurable via tableName
  • JSONB storage — Values are JSON-serialized and stored as JSONB
  • Lazy expiry — Expired entries are cleaned on startup and checked on each get()

WARNING

This adapter currently supports Bun only. Node.js support is planned — PRs are welcome!

@gramio/storage-redis — ioredis Moved to Peer Dependencies

ioredis is no longer bundled as a direct dependency of @gramio/storage-redis. You now need to install it separately:

sh
npm install @gramio/storage-redis ioredis
sh
yarn add @gramio/storage-redis ioredis
sh
pnpm add @gramio/storage-redis ioredis
sh
bun add @gramio/storage-redis ioredis

This gives you full control over the ioredis version and avoids duplication when you're already using it elsewhere in your project.

@gramio/scenes — onEnter() Hook

Run logic when a user enters a scene

Scenes now support an onEnter() method that lets you register a handler to run once when a scene is entered — perfect for sending a welcome message, initializing state, or logging.

ts
import { Scene } from "@gramio/scenes";

const registrationScene = new Scene("registration")
    .onEnter((context) => {
        return context.send("Welcome to the registration wizard! Let's get started.");
    })
    .step("message", (context) => {
        if (context.scene.step.firstTime) return context.send("What's your name?");
        // ...
    });

The handler is fully async-compatible and is awaited before the scene proceeds. Community contribution by @unitoshka.

@gramio/contexts v0.3.1 — Callback Query chatId & UniqueGift TON Support

chatId on callback query context

You no longer need to dig through context.message?.chat?.id to get the chat ID in a callback query handler. The new chatId getter gives you direct access:

ts
bot.on("callback_query", (context) => {
    console.log(context.chatId); // number | undefined
});

Community contribution by @n08i40k.

UniqueGiftInfo adapts to Telegram's new resale fields

Telegram updated the Bot API to support TON (toncoin) payments for unique gift resales. UniqueGiftInfo has been updated with two new properties:

  • lastResaleCurrency"XTR" (Telegram Stars) or "TON" (toncoins)
  • lastResaleAmount — The raw payment amount in the respective currency

The existing lastResaleStarCount now returns the amount only when lastResaleCurrency === "XTR", and undefined otherwise. The @gramio/types dependency was bumped to ^9.3.0 to match.

Documentation & Community

Type-safe storage docs with generics

A whole new section was added to the Storages page covering type-safe storage with generics — basic usage, template literal types, and complex type unions. Both EN and RU versions.

Community contributions

  • Sury validation examples added to scenes .ask() docs alongside existing Zod examples (@unitoshka)
  • Fixed context.scene.next()context.scene.step.next() in scenes docs
  • Fixed undefined language errors in code blocks (curlbash, treebash)
  • Fixed broken autoload plugin link pointing to wrong URL (@ttempaa)
  • Renamed index.mdoverview.md for plugins, storages, and TMA to fix VitePress routing (@ttempaa)
  • Added AGENTS.md with bilingual documentation sync rules

Examples: Forum bot migrates to Cloudflare Workers

The gramio-forum-bot example was fully migrated to Cloudflare Workers with proper HMAC webhook verification via Web Crypto API, token sanitization, message truncation (4096 char limit), and noise filtering for chore(deps) commits. New webhook handlers were added for issue_comment, pull_request_review, and pull_request_review_comment events.