Skip to content

the Telegram client ​

Telegram is the main entry point. it holds your bot token, owns the api proxy, manages transport (polling or webhook), and dispatches incoming updates

ts
import { Telegram } from 'puregram'

const tg = Telegram.fromToken(process.env.TOKEN!)

tg.onMessage(message => message.send('hi!'))

await tg.startPolling()

constructing the client ​

Telegram.fromToken(token, options?) ​

the preferred way. reads sensible defaults and lets you override only what you need:

ts
const tg = Telegram.fromToken(process.env.TOKEN!, {
  retryOnFloodWait: true
})

new Telegram(options) ​

explicit form — useful when you need full control over every option or when constructing programmatically:

ts
const tg = new Telegram({
  token: process.env.TOKEN!,
  apiBaseUrl: 'https://api.telegram.org/bot',
  apiTimeout: 30_000,
  apiRetryLimit: -1
})

options reference ​

optiontypedefaultdescription
tokenstring—bot token from @BotFather
httpClientHttpClientnative fetchpluggable http backend (see below)
botTelegramUser—pre-populate tg.bot, skip the start-time getMe call
allowedUpdatesstring[][] (all)update kinds to receive from telegram
apiBaseUrlstringhttps://api.telegram.org/botapi base url (local bot api, custom proxy)
apiTimeoutnumber30_000per-request timeout in ms
apiWaitnumber3000getUpdates long-poll window in ms
apiRetryLimitnumber-1 (off)max automatic retries per request
apiHeadersRecord<string, string>{}extra headers merged onto every request
useTestDcbooleanfalseroute to the telegram test datacenter
useLocalbooleanfalselocal bot api server mode
retryOnFloodWaitboolean | RetryOnFloodWaitOptionsfalseauto-sleep and retry on 429 responses
swallowDispatchErrorsbooleanfalsesuppress unhandled dispatch errors reaching node's uncaughtException

lifecycle ​

ts
// long polling — starts the bot, calls getMe (populates tg.bot), then loops getUpdates
await tg.startPolling()

// stop polling gracefully
tg.stopPolling()

// webhook — starts the bot and optionally spins up a built-in node:http listener
await tg.startWebhook({ url: 'https://example.com/bot', port: 3000 })

tg.startPolling() accepts a StartPollingOptions object for fine-tuning the polling loop (allowed updates, timeout, limit). details are in the polling deployment guide

tg.bot is populated after the first startPolling() (or start()) call via a getMe request. if you pass a bot option, the getMe call is skipped and tg.bot is pre-populated immediately

pluggable HttpClient ​

the default transport uses node's native fetch. to swap it out — for testing, for proxying, or for a custom retry policy — implement the HttpClient interface:

ts
import type { HttpClient, HttpRequestInput } from 'puregram'

const myClient: HttpClient = {
  async request (input: HttpRequestInput) {
    const response = await fetch(input.url, input.init)

    return {
      status: response.status,
      json: () => response.json()
    }
  },

  // optional — only needed if you use tg.download()
  async download (url, init) {
    const response = await fetch(url, init)

    return { status: response.status, body: response.body }
  }
}

const tg = Telegram.fromToken(process.env.TOKEN!, { httpClient: myClient })

the request method is required. download is optional — when omitted, tg.download() falls back to native fetch

token hygiene

never hardcode a bot token. read it from process.env (a .env file + --env-file .env in node 22 is enough) or from a secrets manager. anyone with the token controls the bot — treat it like a password

see also ​