Webapp Examples¶
Practical patterns for webapp commands and URL generation. Copy, adapt, ship.
JSON API (webapp)¶
Webapps have no user context — design APIs around params, db.bot, or tokens you validate yourself.
let id = params.id
if (!id) {
return res.status(400).json({ error: "id required" })
}
let record = await db.bot.get("item:" + id)
res
.set("Access-Control-Allow-Origin", "*")
.json({ ok: true, record: record || null })
Browsers calling your API
Set CORS headers when a front-end on another origin fetches your webapp. See Headers & Status.
HTML dashboard via res.render()¶
Separate logic from layout — handler command fetches data, template command displays it.
Handler command (showDashboard):
let stats = {
visitors: await db.bot.get("visitors") || 0,
updated: Date.now()
}
res.render("dashboard.html", { data: { stats } })
Template command (dashboard.html):
Send a webapp link in Telegram¶
let url = Webapp.getUrl("dashboard", {
params: { ref: "telegram", lang: "en" }
})
await Api.sendMessage({
chat_id: chat.id,
text: "Open dashboard: " + url
})
Public landing page link¶
For static is_web commands — no sandbox, no res:
let home = Webapp.getUrl("index.html", { public: true })
await Api.sendMessage({
chat_id: chat.id,
text: "Visit our site: " + home
})
The index.html command should contain static HTML (optionally with EJS for bot.username and params). See Public Web.
User-specific action (use webhook, not webapp)¶
When you need user context and signing:
Inside syncProgress:
let progress = await db.user.get("level", 0)
res.json({ ok: true, user_id: user.id, level: progress })
Webapps are unsigned — anyone with the URL can call them. Per-user mutations belong on signed webhooks.