Runnable, idiomatic examples for node-telegram-bot-api v2. Each file imports the
package by its published name (node-telegram-bot-api and
node-telegram-bot-api/node) - exactly how a real consumer would - never via
relative ../src paths.
| File | Shows | Run |
|---|---|---|
01-polling-bot.ts |
Bot with command/hears/on, ctx.reply, managed run() from /node |
BOT_TOKEN=... bun examples/01-polling-bot.ts |
02-api-client.ts |
Low-level Api: getMe, sendMessage, token from env |
BOT_TOKEN=... CHAT_ID=... bun examples/02-api-client.ts |
03-webhook-workers.ts |
webhookCallback as a Cloudflare Workers fetch, with fastAck + waitUntil |
deploy with wrangler (illustrative) |
04-webhook-express.ts |
registerExpressWebhook mounting the route on a (structurally-typed) app |
run inside an Express server (illustrative) |
05-webhook-nextjs.ts |
nextAppWebhook as an App Router export const POST |
app/api/telegram/route.ts in a Next.js app (illustrative) |
06-keyboards.ts |
InlineKeyboardBuilder, ReplyKeyboardBuilder, ctx.answerCallbackQuery |
BOT_TOKEN=... bun examples/06-keyboards.ts |
07-formatting.ts |
EntityBuilder + EntityType + json(), sending entities |
BOT_TOKEN=... CHAT_ID=... bun examples/07-formatting.ts |
08-uploads.ts |
InputFile, fromPath (/node), MediaGroupBuilder |
BOT_TOKEN=... CHAT_ID=... bun examples/08-uploads.ts |
09-middleware.ts |
bot.use for timing, an in-memory session, and an allowlist auth gate |
BOT_TOKEN=... ALLOWED_USERS=111,222 bun examples/09-middleware.ts |
10-resilience-ratelimit.ts |
maxRetries/retryBackoffMs/rateLimit, branching on TelegramApiError/NetworkError |
BOT_TOKEN=... CHAT_ID=... bun examples/10-resilience-ratelimit.ts |
11-longpoll-stream.ts |
Consume longPoll(api, opts, signal) with for await, filter, stop via AbortController |
BOT_TOKEN=... bun examples/11-longpoll-stream.ts |
12-conversation.ts |
A spaceship launch flow with typed session states: name your ship, pick a planet, and launch | BOT_TOKEN=... bun examples/12-conversation.ts |
13-webhook-node-server.ts |
createWebhookServer (/node): a raw node:http webhook server, no framework |
BOT_TOKEN=... WEBHOOK_SECRET=... PUBLIC_URL=https://... bun examples/13-webhook-node-server.ts |
14-photo-video-livephoto.ts |
sendPhoto/sendVideo/sendLivePhoto uploading bytes only (InputFile + fromPath - no URLs, no file_id) |
BOT_TOKEN=... CHAT_ID=... bun examples/14-photo-video-livephoto.ts |
15-rich-message.ts |
RichMessageBuilder, RichTextBuilder, richMessageButton/richListItem, sendRichMessage (blocks + html mode) |
BOT_TOKEN=... CHAT_ID=... bun examples/15-rich-message.ts |
16-sessions.ts |
createSession() + session.get(ctx), durable FileSessionStorage (/node), taggedReplies for a name->email reply flow |
BOT_TOKEN=... bun examples/16-sessions.ts |
17-callback-tracking.ts |
Routing button presses two ways: stateless callback_data paging, vs. expectCallback/matchCallback(ctx, { once: true }) for an oversized, private, one-shot confirmation |
BOT_TOKEN=... bun examples/17-callback-tracking.ts |
18-reply-tracking-lru.ts |
Bounding the reply/press tables with a per-chat LRU: createSession({ replyTracking: { maxEntriesPerChat, maxBytesPerChat, defaultTtlSeconds, slidingTtl } }), evicting least-recently-used markers |
BOT_TOKEN=... bun examples/18-reply-tracking-lru.ts |
The framework webhook examples (03-05) target serverless/framework platforms and
aren't standalone-runnable here, but they typecheck and show the exact wiring.
The self-hosted node:http variant (13) does start a real server locally - put a
TLS-terminating proxy or tunnel in front of it to receive live Telegram traffic.
The examples import the package name, so the package must resolve. Either:
- Build first, then run with Bun/Node:
bun run build # emits dist/ that the package "exports" map points at BOT_TOKEN=... bun examples/01-polling-bot.ts - Or just typecheck them against the source (no build) via the path-mapped
config - the bare-name imports are mapped onto
src/:./node_modules/.bin/tsc -p examples/tsconfig.json --noEmit
- Tokens are read from
process.env.BOT_TOKEN(non-null asserted for brevity); never hardcode a token. CHAT_ID(your user id or a group id) is required by the examples that actually send messages.- Every builder's
.build()returns a wire-readyJson<...>string - the library serializes nothing; you produce the JSON at the call site.