{"post":{"slug":"how-to-build-a-telegram-bot-2026","title":"How to build a Telegram bot in 2026","excerpt":"Telegram bots use the Bot API over webhooks or long-polling. This guide covers a production-ready setup with grammY in 2026, including commands and middleware.","category":"code","date":"March 11, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">T</span>elegram bots are one of the fastest paths from \"idea\" to \"working chatbot\" in 2026. The Bot API is stable, the webhook model is simple, and grammY — the leading TypeScript-first framework — reduces boilerplate to almost nothing. Whether you're building a notification bot, a workflow tool, or a full conversational assistant, the same core patterns apply.\n\n## What changed in 2026\n\n- **grammY v2** is the stable release — breaking changes from v1 are mainly in session and plugin APIs; the middleware model is unchanged.\n- **Bot API 7.x** added refined message reactions, story sharing, and improved inline query response types.\n- **Edge deployments work.** grammY's `webhookCallback` runs natively on Cloudflare Workers and Vercel Edge, making serverless Telegram bots practical.\n- **Long-polling still works fine for development.** No need to expose a local port; `bot.start()` handles it.\n\n## Project setup\n\n```bash\nmkdir telegram-bot && cd telegram-bot\nnpm init -y\nnpm install grammy\n```\n\nCreate `bot.mjs`:\n\n```js\nimport { Bot } from 'grammy';\n\nconst bot = new Bot(process.env.BOT_TOKEN);\n\nbot.command('start', (ctx) => ctx.reply('Hello! I am alive.'));\nbot.command('ping', (ctx) => ctx.reply('Pong!'));\n\nbot.start(); // long-polling for local dev\n```\n\nGet your `BOT_TOKEN` from [@BotFather](https://t.me/BotFather) — `/newbot`, follow the prompts.\n\n## Registering commands\n\n```js\nawait bot.api.setMyCommands([\n  { command: 'start', description: 'Start the bot' },\n  { command: 'ping', description: 'Check latency' },\n  { command: 'help', description: 'Show help' },\n]);\n```\n\nRun this once at deploy time (not every boot). Commands appear in Telegram's `/` autocomplete UI.\n\n## Switching to webhooks for production\n\n```js\nimport { Bot, webhookCallback } from 'grammy';\nimport express from 'express';\n\nconst bot = new Bot(process.env.BOT_TOKEN);\n// ... register handlers ...\n\nconst app = express();\napp.use(express.json());\napp.post(`/webhook/${process.env.BOT_TOKEN}`, webhookCallback(bot, 'express'));\napp.listen(3000);\n\n// Tell Telegram where to send updates:\nawait bot.api.setWebhook(`https://your-domain.com/webhook/${process.env.BOT_TOKEN}`);\n```\n\nIncluding the token in the webhook path provides a simple shared secret — Telegram will only call that URL, and random callers won't know it.\n\n## Sessions for per-user state\n\n```js\nimport { Bot, session } from 'grammy';\n\nconst bot = new Bot(process.env.BOT_TOKEN);\n\nbot.use(session({ initial: () => ({ step: 0, answers: [] }) }));\n\nbot.command('quiz', async (ctx) => {\n  ctx.session.step = 1;\n  await ctx.reply('Question 1: What is 2 + 2?');\n});\n\nbot.on('message:text', async (ctx) => {\n  if (ctx.session.step === 1) {\n    ctx.session.answers.push(ctx.message.text);\n    ctx.session.step = 0;\n    await ctx.reply(`Got it: ${ctx.message.text}`);\n  }\n});\n```\n\nFor persistence, plug in `@grammyjs/storage-redis` or any grammY storage adapter — sessions survive restarts.\n\n## Deployment comparison\n\n| Platform | Polling or webhook | Cold start | Cost |\n|---|---|---|---|\n| VPS (Railway, Hetzner) | Polling or webhook | None | ~$5/mo |\n| Cloudflare Workers | Webhook only | ~0ms | Free tier generous |\n| Vercel Edge | Webhook only | ~50ms | Free tier generous |\n| AWS Lambda | Webhook only | ~200ms | Pay per invocation |\n\nFor bots that need persistent state (DB connections, sessions), a VPS or container is simpler. For stateless notification bots, Workers/Vercel Edge are ideal.\n\n## How to start\n\n1. Create a bot with @BotFather; save the token.\n2. Install grammY; wire up a minimal `bot.command('start', ...)`.\n3. Run `bot.start()` locally to test via long-polling.\n4. Deploy to your chosen platform; call `setWebhook` with the public URL.\n5. Register commands with `setMyCommands` as a one-time deploy step.\n\n## Common mistakes\n\n**Handling `bot.on('message')` without guards.** Every message — including group messages to other bots — will trigger it. Use `bot.on('message:text')` or filter by `ctx.chat.type`.\n\n**Forgetting `await` on `ctx.reply()`.** grammY is async throughout; missing `await` means errors are silently swallowed.\n\n**Polling in production.** Long-polling opens a persistent HTTP connection per process. Under load, switch to webhooks.\n\n**Storing the token in version control.** Use `dotenv` locally and environment secrets in CI/CD.\n\n## What to skip\n\n- **node-telegram-bot-api** — unmaintained, callback-based, no TypeScript types by default.\n- **Storing conversation state in module-level variables** — it breaks on multi-instance deploys. Use sessions.\n- **DIY webhook secret validation** — let grammY's `webhookCallback` handle it; it checks Telegram's `X-Telegram-Bot-Api-Secret-Token` header automatically.\n\n## FAQ\n\n**Do I need a domain for a webhook?**\nYes — Telegram requires HTTPS with a valid certificate. Use Let's Encrypt, Cloudflare, or a platform that provisions TLS automatically. For local dev, long-polling needs no domain.\n\n**Can the same bot talk to multiple chats?**\nYes. `ctx.chat.id` identifies the conversation. Sessions are keyed per chat (or per user) depending on your `getSessionKey` function.\n\n**How do I send proactive messages (not in response to a user)?**\nCall `bot.api.sendMessage(chatId, text)` directly. Store `chatId` from the first `/start` interaction.\n\n**Is grammY production-proven?**\nYes — grammY powers thousands of production bots. Its middleware stack, typed context, and plugin ecosystem are mature.\n\n## Where to go next\n\n- [How to build a Discord bot in 2026](/blog/how-to-build-a-discord-bot-2026)\n- [How to build a Slack app in 2026](/blog/how-to-build-a-slack-app-2026)\n- [How to set up Redis caching in 2026](/blog/how-to-set-up-redis-caching-2026)","hue":306,"metaTitle":"How to Build a Telegram Bot in 2026 (grammY)","metaDescription":"Complete 2026 guide to building a Telegram bot with grammY. Covers webhooks, long-polling, commands, middleware, sessions, and deployment patterns.","keywords":["how to build a telegram bot 2026","grammy telegram bot","telegram bot api 2026","telegram bot webhook setup","telegram bot long polling","telegram bot node.js 2026","grammy middleware","telegram bot commands","telegram bot session","telegram bot deployment 2026"],"tldr":["**Use webhooks in production, long-polling in dev** — webhooks are more efficient; long-polling needs no public URL for local testing.","**grammY is the 2026 default** — typed, middleware-first, ESM-native; works on Node.js, Deno, and Cloudflare Workers.","**Register commands with `setMyCommands`** — shows them in the Telegram UI autocomplete; update on deploy, not on every boot.","**Use sessions for per-user state** — grammY sessions store conversation state cleanly without a global variable mess.","**Skip:** the raw `node-telegram-bot-api` package for new projects — it lacks typing, middleware, and active maintenance in 2026."],"readTime":"4 min read","wordCount":795},"related":[{"slug":"how-to-build-a-discord-bot-2026","title":"How to build a Discord bot in 2026","excerpt":"Discord bots in 2026 use slash commands, interactions, and gateway intents. This guide covers a production-ready setup with discord.js v15 in under an hour.","category":"code","date":"February 22, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":305},{"slug":"how-to-build-a-slack-app-2026","title":"How to build a Slack app in 2026","excerpt":"Slack apps in 2026 use Bolt, socket mode for dev, and event subscriptions for production. This guide gets you from zero to a working slash command in under an hour.","category":"code","date":"March 28, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":307},{"slug":"how-to-build-a-rest-api-in-node-2026","title":"How to build a REST API in Node in 2026: the complete guide","excerpt":"Building a production-ready REST API in Node.js in 2026 means choosing the right framework, structuring routes cleanly, and shipping validation and auth from day one.","category":"code","date":"March 11, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":318}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-build-a-rest-api-in-node-2026","title":"How to build a REST API in Node in 2026: the complete guide","excerpt":"Building a production-ready REST API in Node.js in 2026 means choosing the right framework, structuring routes cleanly, and shipping validation and auth from day one.","category":"code","date":"March 11, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":318},"older":{"slug":"how-to-build-business-credit-2026","title":"How to build business credit in 2026: the step-by-step path from zero","excerpt":"Business credit is separate from personal credit and unlocks better financing terms, higher limits, and liability protection. Here is how to build it fast.","category":"finance","date":"March 11, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":298}}