introduction Bot API 10.3

yaebal is a type-safe, extensible Telegram Bot API framework where the context type accumulates through a single chainable middleware engine.

what it is

yaebal — Yet Another tElegram Bot Api Library — wraps the Telegram Bot API in a chain you build once. you start from a Bot, hang middleware, plugins, routers and enrichment off it, and every step refines the type of the context your handlers see. there is no manual casting and no widening to any in the public surface.

bot.ts
import { Bot, bold, format } from "@yaebal/core";

const loadUser = async (id: number) => ({ id, name: "somebody" });

const bot = new Bot(process.env.BOT_TOKEN!)
  .derive(async (ctx) => ({ user: await loadUser(ctx.from!.id) })) // async, per-request
  .decorate({ version: "1.0.0" })                                  // static, zero cost
  .command("start", (ctx) => ctx.reply("hello 👋"))
  .on("message:text", (ctx) => {
    ctx.user;     // ✅ added by derive, fully typed
    ctx.version;  // ✅ added by decorate
    ctx.text;     // ✅ narrowed to string by the filter query
  });

await bot.start();
try it — your first bot
import { createBot } from "yaebal";

const bot = createBot(process.env.BOT_TOKEN!);

bot.command("start", (ctx) => ctx.reply("hello from yaebal"));

bot.on("message:text", (ctx) => {
  console.log("got message:", ctx.text);
  ctx.reply(`you said: ${ctx.text}`);
});

bot.onStart((me) => console.log("bot ready"));

bot.start();

what it costs

core has zero runtime dependencies and is fetch-first: it runs unmodified on Node 20+, Bun, Deno and edge runtimes like Cloudflare Workers — no shims, no adapter package to pick just to get started. see runtime support for the full matrix and comparison for how that stacks up against grammY, gramio, puregram and telegraf.

the design, borrowed deliberately

yaebal takes one strong idea from each of three existing libraries and keeps them distinct:

sourceideain yaebal
GramIOchainable composerthe context type accumulates through the chain — derive/decorate/install/extend each return an augmented type
grammYfilter querieson("message:text") narrows the context so the filtered field is present and typed
puregramhooks & mediarequest before/after/onError hooks on the Api, plus a clean MediaSource abstraction

see comparison for a full feature matrix against all four.

the core invariants

these are the rules the framework is built to preserve:

  • Bot extends Composer. the bot is the middleware chain — the engine is extended, never forked.
  • derive is async + per-request; decorate is static. derive runs a function on every update to add computed state; decorate attaches a constant value with no per-request cost. they are kept deliberately separate.
  • types flow through the chain. any composer method that enriches the context returns a composer with an augmented context type — never widened to any.
  • plugin dependencies are explicit. a Plugin's required context is expressed in its type, so installing it before its dependency is a compile error, not a runtime surprise.
bot.ts
import { Composer, type Context } from "@yaebal/core";

// Bot extends Composer — there is no separate router.
// derive / decorate / install / extend all return the augmented Bot,
// so the context type accumulates as you chain.

class Bot<C extends Context> extends Composer<C> {}
one engine. because Bot subclasses Composer, you can build feature files as plain standalone Composers with no token, then merge them into the bot with extend — the merged context type comes along.

the package map

core is the only package you need to start. it ships the engine, the context, media, hooks and the webhook handlers:

exportwhat
Bot, Composerthe chainable middleware engine
Contextthe per-update wrapper with send/reply/sendPhoto/…
media, isMediaSource, MediaSourcethe file abstraction
createApi, Api, TelegramErrorthe low-level API client and hooks
webhookCallbackfetch-style webhook handler; Node's nodeWebhookCallback lives in @yaebal/core/node
format, bold, italic, …entity-based message formatting helpers

next

  • getting started — a bot from zero in about a minute
  • core concepts — the composer, derive/decorate, filter queries
  • comparison — how yaebal stacks up against grammY, gramio, puregram and telegraf