⏱️ Lectura: 11 min
Telegram just turned its own infrastructure into hosting for your bot: with Telegram Serverless, the code that responds to a message or a button press runs directly on Telegram’s servers, without you having to spin up a VPS, a container, or a third-party cloud function.
📑 En este artículo
- TL;DR
- What Telegram Serverless is and why it matters
- How the three-layer mental model works
- Practical examples: from hello world to a real case
- How to get started step by step
- Real-world use cases
- Common mistakes and best practices
- Comparison with alternatives
- Going deeper: what happens under the hood
- Frequently Asked Questions
- Does Telegram Serverless fully replace a VPS?
- What happens to the data if my bot gets a sudden traffic spike?
- Can I use external npm libraries inside the modules?
- Do I need a Telegram Business account or a special developer account?
- How do I test a handler before deploying it to production?
- Can schema.js migrations be rolled back?
- References
If you have ever stood up a server just so your bot could answer /start, this platform removes that step entirely: you write JavaScript modules, deploy them with a single command, and Telegram runs them alongside its own API and an included database.
TL;DR
- You’ll understand what Telegram Serverless is and how it replaces a VPS or cloud function for bots.
- You’ll learn the three-layer model: local folder, the Telegram cloud, and the tgcloud CLI.
- You’ll be able to write handlers/message.js that respond to updates using the API and a SQLite database.
- You’ll know how to define tables in schema.js using the SDK’s table() and integer() DSL.
- You’ll deploy with npx tgcloud push and migrate the schema with npx tgcloud migrate.
- You’ll tell when Serverless is a better fit than a traditional VPS or an external cloud function.
- You’ll spot the common mistakes when migrating an existing bot to this model.
What Telegram Serverless is and why it matters
Telegram Serverless is a backend runtime built into the platform: it lets you write the code that processes your bot’s updates (messages, buttons, inline queries) as plain JavaScript modules, without managing any server of your own. Telegram runs it in an isolated V8 sandbox that executes close to the Bot API, which reduces the latency between receiving an update and responding.
The offering solves a problem anyone who has built a bot knows well: for /start to work 24/7, you needed a process that was always on, reachable from the internet, and patched. With Serverless that process doesn’t exist: your code runs on demand and scales automatically with your bot’s traffic.
The platform is not a template for a single kind of app. According to the official documentation, it serves conversational bots with per-user state, the backend of a Mini App, games with leaderboards, and automations that call external HTTP APIs and post the results into a chat.
📌 Note: before writing a single line of code, you have to enable the feature. In @BotFather: open your bot, go to Serverless, and turn it on. That unlocks the CLI token, the handlers, the library, and the database.
How the three-layer mental model works
You work in three places that map one to one. Your local folder holds the versioned code; the Telegram cloud holds the deployed copy of those modules plus your bot’s database; and the tgcloud CLI is the bridge that shows you the differences between the two and syncs them.
You never SSH anywhere. You edit files locally, run npx tgcloud push, and the platform handles the rest. Your bot’s traffic is served by the deployed copy; the database persists between invocations even though each handler run is ephemeral.
A project has only three kinds of code: handlers/ with one file per update type, lib/ with shared code you import from any handler, and schema.js with your database tables.
flowchart TD
A["Carpeta local: handlers, lib, schema.js"] --> B["npx tgcloud push"]
B --> C["La nube de Telegram: modulos + base de datos"]
C --> D["Bot responde updates en produccion"]
When an update arrives (a message, a pressed button, an inline query), Telegram routes it to the corresponding handler (handlers/message.js, handlers/callback_query.js, etc.) and calls its default export. That function talks to the Bot API and the database through the SDK, then returns. An update with no handler to serve it is simply ignored, so you add only the handlers you need.
Practical examples: from hello world to a real case
The simplest possible handler receives a message and replies. It doesn’t touch the database and imports nothing beyond the API SDK:
import { api } from 'sdk';
export default async function (message) {
await api.sendMessage({
chat_id: message.chat.id,
text: `Recibi tu mensaje: ${message.text ?? '(sin texto)'}`,
});
}
This file goes in handlers/message.js. Deployed with npx tgcloud push, it’s already a live bot that echoes every message: there’s no database and no state, so there’s no need to run any migration.
The next example gets closer to a real case: a support bot that counts how many tickets each chat has opened and remembers the last subject. First we define the table in schema.js:
import { table, integer, text } from 'sdk/db';
export const tickets = table('tickets', {
chatId: integer('chat_id').primaryKey(),
abiertos: integer('abiertos').notNull().default(0),
ultimoAsunto: text('ultimo_asunto'),
});
And then the handler that inserts or updates that row on every message, using onConflictDoUpdate to increment the counter atomically:
import { api, db } from 'sdk';
import { tickets } from 'schema';
import { sql } from 'sdk/db';
export default async function (message) {
const chatId = message.chat.id;
const asunto = message.text ?? 'Sin asunto';
const [fila] = await db.insert(tickets)
.values({ chatId, abiertos: 1, ultimoAsunto: asunto })
.onConflictDoUpdate({
target: tickets.chatId,
set: { abiertos: sql`${tickets.abiertos} + 1`, ultimoAsunto: asunto },
})
.returning()
.run();
await api.sendMessage({
chat_id: chatId,
text: `Ticket #${fila.abiertos} registrado para este chat. Asunto: ${fila.ultimoAsunto}`,
});
}
With this you already have a bot with persistent per-chat state, without ever touching a server. The expected result: each new message returns a ticket number that increments and stores the last subject received.
sequenceDiagram
participant U as Usuario
participant T as Telegram
participant H as Handler en V8
participant D as Base de datos
U->>T: envia mensaje
T->>H: entrega el update
H->>D: inserta o actualiza fila
D-->>H: devuelve fila actualizada
H-->>T: llama a api.sendMessage
T-->>U: muestra la respuesta
How to get started step by step
You need Node.js 18 or higher and a bot already registered with @BotFather. First install Node according to your operating system.
Windows:
winget install OpenJS.NodeJS.LTS
macOS:
brew install node
Linux (Debian/Ubuntu):
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
With Node installed, the rest of the steps are identical across all three platforms. First enable Serverless in @BotFather (Bot → Serverless → enable) and then create the project:
npm create @tgcloud/bot soporte_bot
cd soporte_bot
The command scaffolds a folder ready to edit, with an example handler in handlers/message.js and a docs/tgcloud-sdk.md file with the SDK reference. Pass . as an argument if you want to scaffold in the current folder.
Write your schema.js and your handlers as in the examples above, and then deploy:
npx tgcloud push
npx tgcloud migrate
npx tgcloud status
push uploads your modules, migrate applies pending schema changes against the cloud database, and status shows you which version is running in production and whether there are undeployed differences. Use run to test a specific handler before deploying.
Real-world use cases
An AI conversational bot that needs to remember each user’s context between messages fits naturally: state lives in the table, not in the memory of a process that can restart. A Mini App backend can use the same handlers to serve dynamic data to the frontend embedded in Telegram without exposing a separate public endpoint.
For games and tools, the included database works for leaderboards and quizzes without hiring an external engine. And for automations and integrations, the outbound HTTP available in each module lets you call third-party APIs and post the result to a channel, for example a bot that checks a feed at intervals and posts alerts.
Common mistakes and best practices
The most frequent mistake when migrating an existing bot is forgetting that there is no persistent filesystem: any state your bot needs to remember between invocations has to live in the schema.js table, not in a global variable or a temporary file.
⚠️ Watch out: if you run
tgcloud pushwithout having runtgcloud migrateafter adding a new table, the handler will fail when it tries to read from or write to a table that doesn’t yet exist in the deployed database.
Another typical slip is assuming that an update without a handler triggers some error: it’s actually ignored silently, so if your bot doesn’t respond to a type of update, check first whether the corresponding file is missing from handlers/ before suspecting the internal logic.
Finally, treat migrations as reviewed code, not an automatic step: every schema change is recorded and versioned alongside the rest of the project, so a git diff over schema.js should be as readable as any other code change.
Comparison with alternatives
| Option | When to use it | Advantage | Limitation |
|---|---|---|---|
| Telegram Serverless | Bots and Mini Apps with no need for your own infrastructure | Zero servers, database and API included, atomic deploy | Coupled to Telegram, not suitable for logic outside the bot’s ecosystem |
| Your own VPS | Bots that already integrate other in-house services or require full control of the environment | Absolute control of the runtime and the operating system | You have to keep it running, patch it, and scale it manually |
| Cloud Function (Lambda, Cloud Run) | Backends that, beyond Telegram, also serve other clients or channels | Portable across providers, integrates with the rest of a cloud stack | You have to wire up the Bot API, the database, and the credentials yourself |
| Hosting panel (cPanel/Heroku style) | Quick prototypes with a graphical management interface | Visual configuration, little infrastructure knowledge required | Less fine-grained control, dependence on the panel provider |
Going deeper: what happens under the hood
Each handler invocation runs in a lightweight V8 isolate, the same isolation mechanism used by runtimes like Cloudflare Workers or Deno Deploy: a separate execution context per invocation, without the cost of starting a full operating-system process. By running physically close to the Bot API infrastructure, calls to api.* and to the database have fewer network hops than a handler hosted on another cloud provider.
💭 Key point: the deploy is atomic:
tgcloud pushreplaces all modules at once, not file by file, so there’s no window where the bot runs with a mix of old and new code.
The included database is backed by SQLite, which explains why the SDK exposes a table DSL (table(), integer(), text()) instead of asking you for a connection string: there’s no separate database server to configure, just the file managed by the platform.
flowchart LR
A["Editar schema.js y handlers/"] --> B["npx tgcloud push"]
B --> C["npx tgcloud migrate"]
C --> D["npx tgcloud status"]
D --> E["Bot en produccion"]
To confirm a deploy went live, npx tgcloud status shows the deployed version and whether your local copy differs from the cloud; no external panel is needed to verify it.
📖 Summary on Telegram: View summary
Your next step: clone the example project with npm create @tgcloud/bot, add your own table in schema.js, and deploy it with tgcloud push followed by tgcloud migrate to see the full cycle in a real bot.
Frequently Asked Questions
Does Telegram Serverless fully replace a VPS?
For the bot logic itself, yes. If your backend also serves other clients outside Telegram, you’ll still need that separate infrastructure.
What happens to the data if my bot gets a sudden traffic spike?
Execution scales automatically because each invocation runs in its own isolate; there’s no single process that gets overwhelmed.
Can I use external npm libraries inside the modules?
The project is structured as standard JavaScript code in lib/, though external dependencies are subject to what the platform’s V8 sandbox supports.
Do I need a Telegram Business account or a special developer account?
No, a bot registered with @BotFather with the Serverless option enabled from the bot’s panel is enough.
How do I test a handler before deploying it to production?
The CLI includes the run command to execute a specific handler locally before uploading it with push.
Can schema.js migrations be rolled back?
They’re managed as reviewed changes within the normal tgcloud migrate flow, versioned alongside the rest of the project just like any other code file.
References
- Telegram Serverless: official documentation with the full reference for the SDK, the CLI, and the project model.
- Telegram Bot API: reference for the methods available on the SDK’s
apiobject. - SQLite: the database engine that backs the storage included in each project.
- Node.js: runtime required at version 18 or higher to use the
tgcloudCLI.
📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion
0 Comments