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
| library | version |
|---|---|
| yaebal | 0.2.x (pre-1.0) |
| grammY | 1.46.x |
| gramio | 0.15.x |
| puregram | 3.10.x |
| telegraf | 4.16.x |
summary
| library | best at | tradeoff |
|---|---|---|
| yaebal | type-accumulating composer, generated context shortcuts, broad first-party plugin catalog, playground, tests, Bot API 10.3 | pre-1.0, newer ecosystem, smaller community |
| gramio | the closest architectural relative — type-accumulating composer via .extend(), generated contexts, its own test package, Bot API 10.3 | no colon-delimited filter-query DSL; smaller first-party plugin catalog outside the core patterns |
| puregram | codegen'd per-update context classes, type-accumulating .extend(), concurrency + per-chat ordering built into its polling transport | no official scaffolder or test framework; plugin dependencies are checked at runtime, not compile time |
| grammY | large plugin ecosystem, mature docs, official multi-worker concurrent runner, filter queries (the pattern yaebal borrows), Bot API 10.3 | context typing needs a manually-declared "flavor" instead of auto-accumulating; no generated per-update context shortcuts |
| telegraf | the most widely used, huge base of examples and answers, official scaffolder | weakest 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
| feature | yaebal | grammY | gramio | puregram | telegraf |
|---|---|---|---|---|---|
| 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
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("🔥");
});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,installandextendcarry context types forward.- plugin dependencies are explicit and type-checked.
- generated contexts add schema-derived shortcuts such as
ctx.react(). @yaebal/testgives 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.