comparison

a neutral map of popular telegram bot libraries. the short version: choose yaebal when you want type flow, generated contexts, first-party plugins and production tooling in one stack — and gramio is the closest relative in spirit, not a strawman.

methodology. versions are live npm dist-tags.latest lookups at time of writing. capability rows are checked against each project's own docs, changelogs and GitHub releases — not memory or guesswork — with a citation-worthy fact behind every ✅/❌. ⚠️ means we looked and either found mixed signals or nothing conclusive either way; it is not a quiet ❌. found something outdated or wrong? open an issue.

versions compared

libraryversion
yaebal0.2.x (pre-1.0)
grammY1.46.x
gramio0.15.x
puregram3.10.x
telegraf4.16.x

summary

librarybest attradeoff
yaebaltype-accumulating composer, generated context shortcuts, broad first-party plugin catalog, playground, tests, Bot API 10.3pre-1.0, newer ecosystem, smaller community
gramiothe closest architectural relative — type-accumulating composer via .extend(), generated contexts, its own test package, Bot API 10.3no colon-delimited filter-query DSL; smaller first-party plugin catalog outside the core patterns
puregramcodegen'd per-update context classes, type-accumulating .extend(), concurrency + per-chat ordering built into its polling transportno official scaffolder or test framework; plugin dependencies are checked at runtime, not compile time
grammYlarge plugin ecosystem, mature docs, official multi-worker concurrent runner, filter queries (the pattern yaebal borrows), Bot API 10.3context typing needs a manually-declared "flavor" instead of auto-accumulating; no generated per-update context shortcuts
telegrafthe most widely used, huge base of examples and answers, official scaffolderweakest native TypeScript story of the five — custom context types are mostly manual, and its own release history shows no Bot API version bump since the 7.x era

feature matrix

featureyaebalgrammYgramiopuregramtelegraf
type-accumulating context✅⚠️✅✅❌
grammY needs a hand-written "context flavor" generic to get typed custom properties. gramio's and puregram's own docs both describe the same idea yaebal uses — `.extend(plugin)` accumulates types automatically, no casting — confirmed directly from their docs, not guessed. telegraf typically wants a manually-specified custom context type, a well-known pain point in its own GitHub discussions.
generated per-update context shortcuts✅❌✅✅❌
gramio (`@gramio/contexts`) and puregram (its update classes are codegen'd from the schema, ~30 discriminated subclasses) both do this too — yaebal isn't alone here. grammY uses one `Context` class with optional fields instead.
filter queries narrow the type ("message:text")✅✅⚠️✅❌
grammY popularized this exact colon-delimited pattern — it's the library yaebal credits the idea to. puregram's codegen'd `hasX` predicates and filters narrow types the same way. gramio narrows by event/update name through its composer instead of a colon-delimited query string — a related but different mechanism; we didn't confirm an equivalent to the specific "message:text" style. telegraf's `bot.on(...)` doesn't narrow the context type.
plugin dependencies checked before your code even runs✅ compile time⚠️⚠️ compile time (implicit)⚠️ runtime❌
yaebal's `Plugin<In, Out>` makes installing a plugin before its dependency a TypeScript error. gramio's own docs warn "order matters" for `.extend()` chains — using a not-yet-added property is almost certainly also a compile error there, just via natural type accumulation rather than a plugin declaring its own explicit input requirement. puregram has a named `dependsOn: ['session']` mechanism, but per its own docs it throws `PluginMissingDep` when the installer runs — a runtime check, not a type error. we found no equivalent in grammY or telegraf.
first-party test framework✅⚠️✅❌❌
@yaebal/test and GramIO's own `@gramio/test` are close equivalents — both wrap the bot, give you virtual users/chats (`createUser`/`createChat`), intercept outgoing api calls, and let you mock responses (`onApi`) without a real network call. grammY documents test hooks but, by its own community's account, no dedicated test framework exists yet. we found no first-party equivalent for puregram or telegraf.
official concurrent runner (ordered per chat)✅✅⚠️✅ built in⚠️
@grammyjs/runner scales across multiple workers on separate cores, with `sequentialize` enforcing per-chat order — a mature, well-documented equivalent to @yaebal/runner. puregram bakes the same idea into its own polling transport: a `concurrency` limit plus `sequentializeBy`, no separate plugin needed. we found no first-party equivalent documented for gramio or telegraf (community middleware may exist for either).
official scaffolding CLI✅✅✅❌✅
create-yaebal, @grammyjs/create-grammy, create-gramio and telegraf's own create-bot are all official, maintained scaffolders. we found no equivalent for puregram.
in-browser playground on the docs site✅❌❌❌❌
yaebal's docs run examples against mock Telegram updates in-browser. we checked each project's own docs site directly and found no first-party equivalent for any of the four — telegraf's community links out to generic third-party sandboxes (RunKit, CodeSandbox) instead.
zero-dependency, fetch-first core (edge runtimes)✅✅⚠️⚠️⚠️
grammY ships official hosting guides for Cloudflare Workers — both a Deno-native and a Node-compat version. gramio advertises Node/Bun/Deno support in its own tagline, but we found no dedicated Cloudflare Workers guide to confirm edge fetch-handler support specifically. we found no confirmation either way for puregram or telegraf.
Bot API freshness✅ 10.3✅ 10.3✅ 10.3✅ 10.3❌
yaebal regenerates from Telegram's schema on a scheduled job. as of october 2026 the other three active libraries caught up: grammY's v1.46.0 release notes name Bot API 10.3, gramio's changelogs cover 10.3 (rich messages, ephemeral messages), and puregram 3.10 pins `@puregram/api` 10.3.3. telegraf's own GitHub releases show no Bot API bump past the 7.x era, while the spec has since moved to 10.3 — expect real gaps in newer methods and fields.

why yaebal

typed-chain.ts
const bot = createBot(token)
  .install(session({ initial: () => ({ count: 0 }) }))
  .derive(async (ctx) => ({ user: await loadUser(ctx.from?.id) }))
  .on("message:text", (ctx) => {
    ctx.session.count;
    ctx.user;
    ctx.text;
    ctx.react("🔥");
  });
feature-routes.ts
import { Composer, createBot, filters } from "yaebal";

const support = new Composer()
  .filter(filters.regex(/^ticket (.+)$/i), (ctx) =>
    ctx.reply(`ticket created: ${ctx.match[1]}`),
  );

const bot = createBot(process.env.BOT_TOKEN!)
  .extend(support)
  .command("help", (ctx) => ctx.reply("send: ticket <subject>"));

bot.start();
  • the bot class extends the composer class, so there is one middleware engine.
  • derive, decorate, install and extend carry context types forward.
  • plugin dependencies are explicit and type-checked.
  • generated contexts add schema-derived shortcuts such as ctx.react().
  • @yaebal/test gives virtual users/chats and intercepted api calls for ci.
  • production packages cover retry, throttling, runner, webhooks, broadcasts and panels — a broader first-party catalog than any of the four above ship together.

when another library is reasonable

  • choose gramio if your team already uses it — it's the closest match architecturally (type-accumulating .extend(), generated contexts, its own test framework), and the choice mostly comes down to plugin catalog and docs style.
  • choose puregram if you want built-in per-chat-ordered concurrency without a separate runner package, or prefer its plugin/dependency style — just know that dependency ordering fails at runtime, not compile time.
  • choose grammY if you need its large existing plugin ecosystem today, or its official multi-worker concurrent runner is exactly what you already know — its Bot API coverage is current, tied with yaebal.
  • choose telegraf for legacy projects where migration cost dominates type-safety gains, or where the sheer volume of existing examples and answers matters more — but verify any newer Bot API feature you need is actually supported first; its own release history shows no version bump past the 7.x era.

migration paths