TutorialTelegramNode.jsMongoDBCloudflare

Building a Scalable Telegram Bot with Node.js and MongoDB

A step-by-step guide on architecting a Telegram bot that can handle thousands of concurrent users.

March 12, 20258 min readBy Damantha Jasinghe

Building a Telegram bot that can handle thousands of concurrent users requires careful architecture from day one. In this guide I'll walk through the approach I use for the SDBOTS platform.

1. Webhook vs. Polling

Polling is great for development — just call getUpdates in a loop. But for production, webhooks are essential. Telegram pushes updates to your server the moment they arrive, with no delay and no wasted requests.

javascript
// Set your webhook once on startup
await bot.telegram.setWebhook('https://your-domain.com/webhook', {
  secret_token: process.env.WEBHOOK_SECRET,
  allowed_updates: ['message', 'callback_query'],
});

2. MongoDB Schema Design

User state is the heart of any bot. Keep it flat and indexed on userId. Store command context separately so you can implement multi-step flows cleanly.

javascript
const userSchema = new Schema({
  userId:    { type: Number, required: true, unique: true, index: true },
  username:  String,
  state:     { type: String, default: 'idle' },
  context:   { type: Schema.Types.Mixed, default: {} },
  createdAt: { type: Date, default: Date.now },
  lastSeen:  { type: Date, default: Date.now },
});

3. Cloudflare Workers Deployment

Deploying your webhook handler on Cloudflare Workers gives you edge latency worldwide and essentially infinite horizontal scale. Your bot responds from the data center closest to the Telegram server that sent the update.

  • Zero cold starts with Cloudflare's V8 isolate model
  • Free tier handles 100K requests/day — more than enough to start
  • KV storage for rate limiting without hitting MongoDB on every request
  • Environment variables via wrangler.toml secrets

4. Rate Limiting

Without rate limiting you'll hit Telegram's 30 messages/second/chat limit and MongoDB will melt. Use Cloudflare KV for a fast sliding-window counter before touching your database.

javascript
async function rateLimitCheck(userId: number, env: Env) {
  const key = `rl:${userId}:${Math.floor(Date.now() / 1000)}`;
  const count = parseInt(await env.KV.get(key) ?? '0');
  if (count >= 5) return false; // 5 requests per second
  await env.KV.put(key, String(count + 1), { expirationTtl: 2 });
  return true;
}

Wrapping up

This architecture handles tens of thousands of users on a single Cloudflare Worker with MongoDB Atlas free tier. Scale up the database when you need to — the edge layer is already infinite.

D

Damantha Jasinghe

Full-stack developer & bot builder from Sri Lanka. CEO of SD Bots. Writing about Telegram, APIs, and modern web dev.

View portfolio