Skip to content
Back to blog

Building Telegram Bots with Node.js: From Zero to Production

A comprehensive guide to building, deploying, and scaling Telegram bots using Node.js and the Telegram Bot API.

MH
Mahmoud Hashem
· August 20, 2024 · 4 min read
Telegram app interface

Why Telegram bots?

Telegram is uniquely suited for bot development. Unlike other messaging platforms, Telegram offers:

  • A clean, well-documented Bot API
  • No approval process for bot creation
  • Support for rich messages (inline keyboards, media, polls)
  • Webhook and polling modes
  • Free, unlimited messaging
  • A built-in payment system

For businesses, Telegram bots are an excellent way to provide customer support, send notifications, and build conversational interfaces.

Creating your bot

Step 1: Register with BotFather

  1. Open Telegram and search for @BotFather
  2. Send /newbot
  3. Choose a name and username (must end in bot)
  4. Save the API token BotFather gives you

Step 2: Set up your project

mkdir my-telegram-bot && cd my-telegram-bot
npm init -y
npm install node-telegram-bot-api express dotenv

Step 3: Your first bot

import TelegramBot from 'node-telegram-bot-api';
import dotenv from 'dotenv';

dotenv.config();

const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });

bot.onText(/\/start/, (msg) => {
  const chatId = msg.chat.id;
  bot.sendMessage(chatId, 'Welcome! I can help you track orders. Send /track <order_number> to get started.');
});

bot.onText(/\/track (.+)/, (msg, match) => {
  const chatId = msg.chat.id;
  const orderNumber = match[1];
  bot.sendMessage(chatId, `Looking up order ${orderNumber}...`);
  // Fetch order status and respond
});

console.log('Bot is running...');

Using inline keyboards

Inline keyboards are one of Telegram’s most powerful features — they let you add interactive buttons to messages:

bot.onText(/\/menu/, (msg) => {
  const chatId = msg.chat.id;
  
  const options = {
    reply_markup: {
      inline_keyboard: [
        [
          { text: 'Track Order', callback_data: 'track' },
          { text: 'Contact Support', callback_data: 'support' },
        ],
        [
          { text: 'FAQ', callback_data: 'faq' },
          { text: 'Settings', callback_data: 'settings' },
        ],
      ],
    },
  };
  
  bot.sendMessage(chatId, 'How can I help you?', options);
});

bot.on('callback_query', (query) => {
  const chatId = query.message.chat.id;
  const action = query.data;
  
  switch (action) {
    case 'track':
      bot.sendMessage(chatId, 'Please enter your order number:');
      break;
    case 'support':
      bot.sendMessage(chatId, 'A support agent will join this chat shortly.');
      break;
    case 'faq':
      bot.sendMessage(chatId, 'Check our FAQ at https://example.com/faq');
      break;
  }
  
  bot.answerCallbackQuery(query.id);
});

Switching to webhooks for production

Polling is fine for development, but for production you should use webhooks. They’re more efficient and scalable.

import express from 'express';
import TelegramBot from 'node-telegram-bot-api';

const app = express();
app.use(express.json());

const bot = new TelegramBot(process.env.BOT_TOKEN);

// Set webhook
bot.setWebHook(`${process.env.WEBHOOK_URL}/bot${process.env.BOT_TOKEN}`);

// Handle webhook requests
app.post(`/bot${process.env.BOT_TOKEN}`, (req, res) => {
  bot.processUpdate(req.body);
  res.sendStatus(200);
});

// Bot handlers
bot.onText(/\/start/, (msg) => {
  bot.sendMessage(msg.chat.id, 'Hello! How can I help you?');
});

app.listen(3000, () => console.log('Server running on port 3000'));

Session management

For conversational bots, you need to track conversation state per user. I use Redis for this:

import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

async function getSession(userId) {
  const session = await redis.get(`session:${userId}`);
  return session ? JSON.parse(session) : { state: 'initial', data: {} };
}

async function saveSession(userId, session) {
  await redis.set(`session:${userId}`, JSON.stringify(session), 'EX', 3600);
}

bot.on('message', async (msg) => {
  const userId = msg.from.id;
  const session = await getSession(userId);
  
  // Handle message based on session state
  switch (session.state) {
    case 'initial':
      // Show main menu
      break;
    case 'awaiting_order_number':
      session.data.orderNumber = msg.text;
      const status = await getOrderStatus(msg.text);
      bot.sendMessage(userId, `Order ${msg.text} status: ${status}`);
      session.state = 'initial';
      break;
  }
  
  await saveSession(userId, session);
});

Deployment with Docker

FROM node:20-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --production

COPY . .

EXPOSE 3000

CMD ["node", "index.js"]
# docker-compose.yml
version: '3'
services:
  bot:
    build: .
    ports:
      - "3000:3000"
    env_file: .env
    depends_on:
      - redis
    restart: unless-stopped
  
  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    restart: unless-stopped

volumes:
  redis-data:

Best practices

  1. Always handle errors: Unhandled errors will crash your bot and miss messages
  2. Use queues for high volume: If your bot handles thousands of messages, use a message queue
  3. Implement rate limiting: Telegram limits you to 30 messages per second
  4. Log everything: You’ll need logs to debug issues with specific users
  5. Use TypeScript: Type safety prevents many runtime errors
  6. Set up monitoring: Use a health check endpoint and uptime monitoring

Conclusion

Telegram bots are one of the easiest and most powerful ways to build conversational interfaces. With Node.js and the Bot API, you can go from zero to production in a matter of hours. The key to a great bot is not the technology — it’s designing a conversation flow that genuinely helps your users.

#Telegram #Node.js #Bots #Development

Related articles

Let's build something great together

Ready to automate your business processes and save hundreds of hours every month? Let's talk about your project.