Skip to main content
The Bot class is the single most important class in grammY. It represents your bot and provides methods for handling updates, registering middleware, and managing the bot’s lifecycle.

Constructor

Creates a new Bot instance with the given token.
const bot = new Bot(token, config?)
token
string
required
The bot’s token as acquired from @BotFather
config
BotConfig<C>
Optional configuration properties for the bot

Example

import { Bot } from 'grammy'

// Simple usage
const bot = new Bot('YOUR_BOT_TOKEN')

// With configuration
const bot = new Bot('YOUR_BOT_TOKEN', {
  client: {
    apiRoot: 'https://api.telegram.org'
  },
  botInfo: cachedBotInfo // Skip getMe call
})

Properties

token
string
The bot’s authentication token (read-only)
api
Api
Full access to the Telegram Bot API. Use this to call any Bot API method.
bot.api.sendMessage(chatId, 'Hello!')
Note: Prefer using ctx.api inside middleware when you have access to the context object.
botInfo
UserFromGetMe
Information about the bot itself as retrieved from api.getMe(). Only available after the bot has been initialized via await bot.init(), or after a manual value has been set.Starting the bot will always perform initialization automatically unless a manual value is already set.
errorHandler
ErrorHandler<C>
The bot’s error handler that is invoked whenever middleware throws an error. Set your own error handler via bot.catch.

Middleware Methods

use

Registers middleware that receives all updates.
bot.use(middleware: Middleware<C>): Composer<C>
middleware
Middleware<C>
required
The middleware function(s) to register. Often used to install plugins like session middleware.
Example:
bot.use(session())
bot.use((ctx, next) => {
  console.log('Update received:', ctx.update.update_id)
  return next()
})

on

Registers middleware for specific update types using filter queries.
bot.on(filter: FilterQuery | FilterQuery[], ...middleware: Middleware[]): Composer
filter
FilterQuery | FilterQuery[]
required
The filter query (or array of queries) to match. Examples:
  • 'message' - All message updates
  • 'message:text' - Only text messages
  • 'message:entities:url' - Messages with URL entities
  • ':text' - Text messages and channel posts
  • ['message:text', 'edited_message:text'] - Multiple filters (OR)
middleware
Middleware<Filter<C, Q>>[]
required
Middleware function(s) to execute when the filter matches
Example:
// Listen for text messages
bot.on('message:text', (ctx) => {
  console.log('Text:', ctx.message.text)
})

// Listen for photos
bot.on('message:photo', (ctx) => {
  console.log('Photo received')
})

// Multiple filters
bot.on(['message:text', 'message:photo'], (ctx) => {
  ctx.reply('Got text or photo!')
})

command

Registers middleware for specific bot commands.
bot.command(command: string | string[], ...middleware: Middleware[]): Composer
command
string | string[]
required
The command(s) to match (without the leading /)
middleware
CommandMiddleware<C>[]
required
Middleware to execute when the command is found. The command arguments are available via ctx.match.
Example:
bot.command('start', (ctx) => {
  ctx.reply('Welcome! Use /help to see available commands.')
  // Deep linking payload available in ctx.match
})

bot.command('help', (ctx) => {
  ctx.reply('Available commands: /start, /help')
})

hears

Registers middleware for messages matching text or regular expressions.
bot.hears(trigger: string | RegExp | Array<string | RegExp>, ...middleware: Middleware[]): Composer
trigger
string | RegExp | Array<string | RegExp>
required
The text or regex pattern to match against message text or captions
middleware
HearsMiddleware<C>[]
required
Middleware to execute on match. For regex triggers, ctx.match contains the RegExpMatchArray.
Example:
// Exact text match
bot.hears('hello', (ctx) => {
  ctx.reply('Hello to you too!')
})

// Regex match
bot.hears(/\/echo (.+)/, (ctx) => {
  const text = ctx.match[1] // Text after /echo
  ctx.reply(text)
})

reaction

Registers middleware for message reaction updates.
bot.reaction(reaction: ReactionType | ReactionType[], ...middleware: Middleware[]): Composer
reaction
ReactionTypeEmoji['emoji'] | ReactionType | Array
required
The reaction emoji or reaction type to match
Note: You must enable message_reaction updates in allowed_updates for your bot to receive reaction updates. Example:
bot.reaction('👍', (ctx) => {
  console.log('User gave thumbs up!')
})

bot.reaction(['👍', '👎'], (ctx) => {
  console.log('User reacted with thumbs up or down')
})

callbackQuery

Registers middleware for callback queries (inline button presses).
bot.callbackQuery(trigger: string | RegExp | Array<string | RegExp>, ...middleware: Middleware[]): Composer
trigger
string | RegExp | Array<string | RegExp>
required
The callback data to match
Example:
bot.callbackQuery('button-payload', async (ctx) => {
  await ctx.answerCallbackQuery('Button clicked!')
  await ctx.editMessageText('You clicked the button')
})

chatType

Registers middleware for specific chat types.
bot.chatType(chatType: Chat['type'] | Chat['type'][], ...middleware: Middleware[]): Composer
chatType
'private' | 'group' | 'supergroup' | 'channel' | Array
required
The chat type(s) to match
Example:
// Only private chats
bot.chatType('private').command('start', (ctx) => {
  ctx.reply('Welcome to the private chat!')
})

// Groups and supergroups
bot.chatType(['group', 'supergroup'], (ctx) => {
  console.log('Group message')
})

Running the Bot

start

Starts the bot using long polling.
await bot.start(options?: PollingOptions): Promise<void>
options
PollingOptions
Options for configuring long polling
Important Notes:
  • The returned Promise will never resolve except if your bot is stopped
  • You don’t need to await the call to bot.start, but remember to catch potential errors via bot.catch
  • This uses simple long polling suitable for small to medium bots
  • For high-load bots (>5K messages/hour) or bots with long-running operations, use @grammyjs/runner instead
Example:
import { Bot } from 'grammy'

const bot = new Bot('YOUR_BOT_TOKEN')

bot.command('start', (ctx) => ctx.reply('Hello!'))

// Start with default options
bot.start()

// Start with custom options
bot.start({
  allowed_updates: ['message', 'callback_query'],
  drop_pending_updates: true,
  onStart: (botInfo) => {
    console.log(`Bot @${botInfo.username} started!`)
  }
})

stop

Stops the bot from long polling.
await bot.stop(): Promise<void>
All middleware currently executing may complete, but no further getUpdates calls will be performed. The current getUpdates request will be cancelled. This method also confirms the last received update to the Telegram servers by calling getUpdates one last time with the latest offset value. Example:
// Graceful shutdown
process.on('SIGINT', async () => {
  console.log('Stopping bot...')
  await bot.stop()
  console.log('Bot stopped')
  process.exit(0)
})

init

Initializes the bot by fetching bot information.
await bot.init(signal?: AbortSignal): Promise<void>
signal
AbortSignal
Optional AbortSignal to cancel the initialization
This method is called automatically when starting the bot. You usually don’t need to call it manually unless you want to access bot.botInfo before starting. Example:
const bot = new Bot('YOUR_BOT_TOKEN')

// Initialize to access bot info
await bot.init()
console.log(`Bot username: @${bot.botInfo.username}`)

Utility Methods

catch

Sets the bot’s error handler for long polling.
bot.catch(errorHandler: ErrorHandler<C>): void
errorHandler
(error: BotError<C>) => unknown
required
Function that handles middleware errors. Receives a BotError object containing both the error and the context.
Example:
bot.catch((err) => {
  const ctx = err.ctx
  console.error(`Error while handling update ${ctx.update.update_id}:`)
  const e = err.error
  if (e instanceof GrammyError) {
    console.error('Error in request:', e.description)
  } else if (e instanceof HttpError) {
    console.error('Could not contact Telegram:', e)
  } else {
    console.error('Unknown error:', e)
  }
})

handleUpdate

Processes a single update object. This is an internal method used by grammY.
await bot.handleUpdate(update: Update, webhookReplyEnvelope?: WebhookReplyEnvelope): Promise<void>
update
Update
required
An update from the Telegram Bot API
webhookReplyEnvelope
WebhookReplyEnvelope
Optional webhook reply envelope for responding to webhook requests
You typically only use this method when:
  • Writing a library on top of grammY
  • Running the bot on webhooks
  • Implementing custom update handling logic

isInited

Checks if the bot has been initialized.
bot.isInited(): boolean
return
boolean
true if bot information is set, false otherwise

isRunning

Checks if the bot is currently running via built-in long polling.
bot.isRunning(): boolean
return
boolean
true if the bot is running, false otherwise
Note: This only applies to bot.start(). It doesn’t reflect webhook server status or grammY runner status.

Advanced Methods

The Bot class inherits all methods from Composer, including:
  • filter() - Filter updates with custom predicates
  • drop() - Inverse filtering
  • fork() - Run middleware concurrently
  • lazy() - Generate middleware dynamically
  • route() - Branch between different middleware
  • branch() - Conditional branching
  • errorBoundary() - Install error boundaries
See the Composer API reference for details.

Complete Example

import { Bot, GrammyError, HttpError } from 'grammy'

const bot = new Bot('YOUR_BOT_TOKEN')

// Middleware
bot.use(async (ctx, next) => {
  console.log(`Update ${ctx.update.update_id} received`)
  await next()
})

// Commands
bot.command('start', (ctx) => {
  ctx.reply('Welcome! I am your bot.')
})

bot.command('help', (ctx) => {
  ctx.reply('Send me any message and I will echo it back!')
})

// Message handlers
bot.on('message:text', (ctx) => {
  ctx.reply(`You said: ${ctx.message.text}`)
})

bot.on('message:photo', (ctx) => {
  ctx.reply('Nice photo!')
})

// Callback queries
bot.callbackQuery('button_1', async (ctx) => {
  await ctx.answerCallbackQuery('Button 1 clicked!')
  await ctx.editMessageText('You clicked button 1')
})

// Error handling
bot.catch((err) => {
  const ctx = err.ctx
  console.error(`Error while handling update ${ctx.update.update_id}:`)
  const e = err.error
  if (e instanceof GrammyError) {
    console.error('Error in request:', e.description)
  } else if (e instanceof HttpError) {
    console.error('Could not contact Telegram:', e)
  } else {
    console.error('Unknown error:', e)
  }
})

// Start the bot
bot.start({
  onStart: (botInfo) => {
    console.log(`Bot @${botInfo.username} is running!`)
  }
})

See Also

Build docs developers (and LLMs) love