@tma.sh/sdk
v0.2.1
Published
SDK for Telegram Mini Apps on TMA -- auth, KV storage, and framework bindings
Readme
Overview
@tma.sh/sdk is built specifically for and tightly coupled with the TMA.sh edge hosting platform. It provides a comprehensive suite of tools for the next generation of Telegram Mini Apps (TMAs). Built with Cloudflare edge computing in mind, this SDK bridges the gap between your static frontend UI (React/Svelte), your edge backend APIs, and the native Telegram Bot ecosystem.
Important Architectural Note: TMA.sh is an edge hosting platform, not a Node.js server environment. It expects a Static Frontend SPA (e.g., standard Vite, React, or SvelteKit configured for static export) and compiles your backend/bot logic into highly-optimized Cloudflare Workers. It does not host Server-Side Rendered (SSR) full-stack frameworks (like standard Next.js).
Whether you're authenticating users, mutating data in a globally distributed KV store, or orchestrating complex chat flows with your Telegram bot, @tma.sh/sdk gives you a type-safe, ergonomic developer experience.
Features
- ⚡️ Edge-Native Bot Framework: Build fast, serverless Telegram Bots that run on Cloudflare Workers.
- 🔐 Automatic Authentication: Zero-config Telegram WebApp
initDataHMAC validation on your backend. - 🌍 Global KV Storage: Read and write persistent JSON data from any environment (Client, Bot, or Backend).
- 🚀 Native UI Hooks: First-class React and Svelte hooks for Telegram components (
MainButton,BackButton,HapticFeedback,start_param). - 📡 Automatic Fetch Wrapper: Fetch from your custom APIs with the user's secure TMA JWT injected automatically.
- 💬 State Management: Built-in KV-backed session middleware for bot conversations.
- 📤 Outbound Messaging:
TelegramApiClientfor pushing messages from your own server (Next.js, Express, etc.). - 🛡 Auth Middleware: Drop-in Hono middleware (
requireUser) for protecting your edge API routes with JWT verification.
Installation
npm install @tma.sh/sdk
# or
pnpm add @tma.sh/sdk
# or
yarn add @tma.sh/sdk🏗 Frontend SDK (React & Svelte)
The frontend SDK handles WebApp initialization, authentication, global state, and native Telegram UI components.
React
Wrap your app in the <TMAProvider> to establish secure validation via Telegram's initData.
// app.tsx
import {
TMAProvider,
useTelegramAuth,
useMainButton,
useBackButton,
useStartParam,
useHapticFeedback,
useThemeParams,
} from "@tma.sh/sdk/react";
function App() {
return (
<TMAProvider config={{ projectId: "prj_12345" }}>
<Dashboard />
</TMAProvider>
);
}
function Dashboard() {
const { user, isLoading } = useTelegramAuth();
const startParam = useStartParam(); // e.g. "campaign_twitter"
const haptics = useHapticFeedback();
const theme = useThemeParams();
// Natively mount the Telegram floating UI button
useMainButton({
text: "Complete Purchase",
onClick: () => {
haptics?.notificationOccurred("success");
console.log("Bought!");
},
});
// Show the native back button on sub-pages
useBackButton({
onClick: () => history.back(),
});
if (isLoading) return <p>Loading...</p>;
if (!user) return <p>Please open inside Telegram.</p>;
return (
<div>
<h1>Hello, {user.firstName}!</h1>
{startParam && <p>Referred by: {startParam}</p>}
</div>
);
}Available React Hooks
| Hook | Description |
|------|-------------|
| useTelegramAuth() | Access authenticated user, JWT, loading state, and errors from <TMAProvider>. |
| useMainButton(options) | Mount and manage the native Telegram floating MainButton. Auto-cleans on unmount. |
| useBackButton(options) | Mount and manage the native Telegram BackButton. Auto-cleans on unmount. |
| useStartParam() | Read the start_param from the launch URL (t.me/bot/app?startapp=VALUE). |
| useHapticFeedback() | Access the device's haptic engine (impactOccurred, notificationOccurred, selectionChanged). |
| useThemeParams() | Access the user's active Telegram theme colors (bg_color, text_color, etc.). |
| useTelegramWebApp() | Access the raw low-level window.Telegram.WebApp object. |
Svelte
In Svelte, configure the provider in your root layout and use our native lifecycle hooks.
<!-- +layout.svelte -->
<script lang="ts">
import { initTMAProvider } from "@tma.sh/sdk/svelte";
initTMAProvider({ projectId: "prj_12345" });
</script>
<slot /><!-- +page.svelte -->
<script lang="ts">
import {
getTMAAuth,
useMainButton,
useBackButton,
getStartParam,
getHapticFeedback,
getThemeParams,
} from "@tma.sh/sdk/svelte";
const auth = getTMAAuth();
const startParam = getStartParam();
const haptics = getHapticFeedback();
useMainButton({
text: "Submit",
onClick: () => {
haptics?.notificationOccurred("success");
},
});
useBackButton({
onClick: () => history.back(),
});
</script>
{#if $auth.isLoading}
<p>Loading...</p>
{:else if $auth.user}
<p>Welcome, {$auth.user.firstName}</p>
{#if startParam}
<p>Referred by: {startParam}</p>
{/if}
{/if}Available Svelte Helpers
| Helper | Description |
|--------|-------------|
| initTMAProvider(config) | Initialize authentication. Call once in root +layout.svelte. |
| getTMAAuth() | Access the reactive auth store ($auth.user, $auth.jwt, etc.). |
| useMainButton(options) | Mount/manage the native MainButton via onMount/onDestroy. |
| useBackButton(options) | Mount/manage the native BackButton via onMount/onDestroy. |
| getStartParam() | Read the start_param from the launch URL. |
| getHapticFeedback() | Access the haptic engine. |
| getThemeParams() | Access the user's Telegram theme colors. |
| getTelegramWebApp() | Access the raw window.Telegram.WebApp object. |
Accessing KV & Secure APIs
Once authenticated by the Provider, you can access your project's Edge KV store natively from the client, or use the fetch helper proxy which automatically injects your secure JWT bearer token.
import { createTMA } from "@tma.sh/sdk";
const tma = createTMA({ projectId: "prj_12345" });
// 1. Write to the global KV
await tma.kv.set("theme", "dark");
const theme = await tma.kv.get("theme");
// 2. Fetch from your custom Backend API (auto-injects Auth JWT)
const response = await tma.fetch("/api/user-data", { method: "POST" });🤖 Bot SDK
The bot SDK allows you to define declarative Telegram Bot handlers. When your code is pushed to TMA.sh, the platform looks specifically for bot/index.ts (or .js) and compiles it directly into an edge-optimized Cloudflare Worker.
Defining Handlers
// bot/index.ts
import { defineBot, session } from "@tma.sh/sdk/bot";
export default defineBot({
description: "I am a TMA-powered Edge Bot!",
// KV-backed session state enabled out-of-the-box
middleware: [session({ ttl: 86400 })],
commands: [
{
command: "start",
description: "Start the bot",
async handler(ctx) {
await ctx.reply("Welcome to our Mini App!", {
reply_markup: {
inline_keyboard: [
[{ text: "Open App", web_app: { url: ctx.env.WEB_APP_URL } }],
],
},
});
// Store conversation state using KV-backed session
ctx.session!.startedAt = Date.now();
},
},
],
// Fallback handler for all other text messages
async onMessage(ctx) {
if (ctx.text) {
await ctx.reply(`You said: ${ctx.text}`);
}
},
async onCallbackQuery(ctx) {
if (ctx.callbackData === "buy") {
await ctx.answerCallbackQuery("Processing...");
await ctx.editMessageText("Payment initiated!");
}
},
});🖥 Backend SDK
Similar to the Bot router, TMA.sh looks strictly for server/api/index.ts (or .js) to host your backend. The @tma.sh/sdk/server utilities provide strict cryptographic validation of incoming Telegram requests and outbound pushes.
Note: If you choose to host your own separate full-stack server (e.g. Next.js on Vercel) external to TMA.sh, you can still use these utilities there!
Protecting Routes with requireUser
The requireUser middleware verifies TMA JWTs via JWKS and injects the authenticated user into the Hono context. When deployed to TMA.sh, it uses a locally injected JWKS binding for zero-latency verification.
// server/api/index.ts
import { Hono } from "hono";
import { requireUser } from "@tma.sh/sdk/server";
const app = new Hono();
// All /api/* routes require a valid TMA JWT
app.use("/api/*", requireUser());
app.get("/api/me", (c) => {
const user = c.get("user");
return c.json({
telegramId: user.telegramId,
name: user.firstName,
projectId: user.projectId,
});
});
export default app;Outbound API Actions
Want to push a message or photo to a specific user after an external webhook (e.g. Stripe checkout) succeeds on your backend? Use the TelegramApiClient.
import { createTelegramApiClient } from "@tma.sh/sdk/server";
const bot = createTelegramApiClient(process.env.BOT_TOKEN!);
// Send a text message
await bot.sendMessage(userChatId, "Your payment was successful! 🎉");
// Send a photo
await bot.sendPhoto(userChatId, "https://example.com/receipt.png", {
caption: "Here's your receipt",
});
// Call any Telegram Bot API method
await bot.call("sendSticker", {
chat_id: userChatId,
sticker: "CAACAgIAA...",
});Validating Users Manually
If you need to validate deep-link query parameters manually, you can use our built-in secure HMAC-SHA256 validator algorithm without any external API calls:
import { validateInitData } from "@tma.sh/sdk/server";
// Verifies authenticity cryptographically against Telegram's specification
const user = await validateInitData(rawInitDataString, process.env.BOT_TOKEN!);
if (user) {
console.log("Validated securely!", user.telegramId);
}Server-Side KV Access
Wrap a Cloudflare KV namespace binding with typed methods:
import { createKV } from "@tma.sh/sdk/server";
const kv = createKV(c.env.KV);
await kv.set("user:123", { name: "Alice" }, 3600); // with TTL
const user = await kv.get<{ name: string }>("user:123");
await kv.delete("user:123");
const keys = await kv.list("user:");📦 Entry Points
| Import Path | Environment | Description |
|-------------|------------|-------------|
| @tma.sh/sdk | Browser | Core client: createTMA(), auth, KV, WebApp types |
| @tma.sh/sdk/react | Browser | React Provider, hooks, re-exports core client |
| @tma.sh/sdk/svelte | Browser | Svelte Provider, hooks, re-exports core client |
| @tma.sh/sdk/server | Edge/Node | Auth middleware, KV wrapper, TelegramApiClient, validateInitData |
| @tma.sh/sdk/bot | Edge | defineBot, session middleware, bot context types |
