Как стать автором
Поиск
Написать публикацию
Обновить

Как создать свое первое приложение Telegram mini app: Next.js, React, Telegram SDK

Уровень сложностиСредний
Автор оригинала: Диана Аглямутдинова

В этом гайде я покажу как создать Web интерфейс для вашего бота в Telegram:

  • ⚙️ Как создать Next.js проект (BFF — backend for frontend)

  • ☁️ Deploy проект на Vercel

  • 🤖 Как добавить Telegram Mini app для вашего бота.

  • 🔐 Как авторизировать юзера через Telegram’s Web App initData

  • 💬 Как использовать основные функции Telegram SDK: Поделиться, задать нативные стили, как управлять навигацией(кнопкаи или жест "назад")

  • 💡 Как использовать нативные всплывающие окна в Телеграм.

В конце этого гайда у вас будет готовое приложение Telegram Mini App, которое открывается из вашего бота.

🧱 1. Настройка проекта

Project schema
Project schema

🛠 Что нам понадобится?

  1. Создать бота с помощью @BotFather и получить ваш BOT_TOKEN

2. Создать приложение Next.js

Откройте терминал в папке вашего проекта и выполните команду ниже и следйте инструкциям установки проекта.

npx create-next-app@latest

🏗️ Использование Next.js как BFF (Backend-for-Frontend) с помощью App Router. Ниже структура проекта, где лежат фронтенд страницы, а где бэкэнд эндпоинты.

3. Опубликовать проект на GitHub

Перейдите на GitHub, создайте новый репозиторий и следуйте инструкциям в разделе Quick setup, чтобы отправить ваш локальный проект в этот репозиторий.

4. Разверните (задеплойте) ваше приложение на Vercel.

Telegram не размещает ваш сайт — ваш Mini App должен быть размещён на общедоступном хостинге.

Создайте аккаунт на Vercel

  • Привяжите свой GitHub-аккаунт

  • Выберите только что созданный репозиторий

  • Следуйте инструкциям, чтобы развернуть проект

На этом этапе ваше приложение уже должно быть доступно в интернете — и готово к привязке к вашему Telegram-боту.

Также оно будет автоматически переразвёртываться после каждого изменения в удалённом репозитории.


🪄 2. Set Up a Mini App

Метод 1: через команду /newapp

  1. Отправьте команду /newapp боту @BotFather

  2. Выберите вашего бота (например, @YourAppBot)

  3. Укажите название и описание для вашего Web App

  4. Вставьте URL размещённого Mini App (например, ссылку с Vercel)

⚙️ Метод 2: через веб-интерфейс BotFather (приложение Mini App)

  1. Откройте приложение BotFather Mini App(кнопка открыть в боте)

  2. Перейдите в раздел My Bots и выберите нужного бота

  3. Перейдите в: Settings → Mini Apps

  4. Нажмите Main App, включите его и вставьте URL вашего Mini App

  5. (Опционально) Создайте Direct Link — это ссылка, которая будет напрямую открывать Mini App в Telegram (например, t.me/your_bot_name/...)

  6. (Опционально) Включите кнопку меню, это кнопка слева внутри вашего бота.

После настройки кнопка «Open» справа будет запускать ваш Mini App прямо внутри Telegram 🎉


🔐 3. Авторизация пользователя с помощью Telegram Mini App SDK

  1. Frontend: Получите initData с помощью SDK и отправьте его на ваш backend через API-запрос.

  2. Backend: Проверьте initData, используя токен вашего бота.

  3. Если проверка успешна: пользователь авторизован! Вы можете доверять данным пользователя, содержащимся в initData.

Установите SDK:

npm install @telegram-apps/sdk-react

Поскольку Telegram внедряет объект Telegram только в браузере, необходимо использовать SDK только внутри клиентского компонента — иначе возникнут проблемы с серверным рендерингом (SSR).

Используйте next/dynamic, чтобы импортировать логику Telegram с отключённым SSR:

const TelegramInit = dynamic(() => import('../telegram/client/TelegramInit'), {
  ssr: false,
});

Сохраните rawInitData для авторизации и отправляйте его на ваш backend для авторизации API-запросов.

import { useRawInitData } from '@telegram-apps/sdk-react';

export default function TelegramInit() {
  const rawInitData = useRawInitData();
  useEffect(() => {
    if (rawInitdata) {
      localStorage.setItem('token', rawInitdata);
    }
  }, [rawInitdata]);
}
const res = await fetch(url, {
  method: method,
  headers: {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${localStorage.getItem('token')}`,
  },
  body: body ? JSON.stringify(body) : undefined,
});

Также вы можете получить полезную информацию о пользователе из Telegram (first_name, last_name, is_bot, photo_url и др.).

import { parse } from '@telegram-apps/init-data-node/web';
const initData = localStorage.getItem('token');
    if (initData) {
      try {
        const parsedData = parse(initData);
        if (parsedData.user) {
          createUserInDB(parsedData.user);
          setUser(parsedData.user);
        }
      } catch (error) {
        console.error('Failed to parse user data:', error);
      }

Проверьте initData на сервере с помощью Telegram Mini Apps SDK:

  • token — это initData, который вы получаете из заголовков запроса

  • BOT_TOKEN — токен вашего бота, полученный от BotFather

import { isValid } from '@telegram-apps/init-data-node/web';
...
const isAuthorized = isValid(token, process.env.BOT_TOKEN!);

Если проверка прошла успешно, вы можете безопасно использовать данные пользователя для операций с базой данных.

🔐 Не забудьте: настройте переменную окружения BOT_TOKEN

Чтобы безопасно использовать токен вашего бота как в среде разработки, так и в продакшене:

🧪 1. Добавьте его в локальное окружение

Создайте файл .env.local в корне вашего Next.js проекта и добавьте в него:

BOT_TOKEN=your_telegram_bot_token_here

This keeps your token out of version control and safe from exposure.

☁️ 2. Добавьте его в переменные окружения Vercel (Environment Variables)

Чтобы ваш бот работал в продакшене:

  1. Перейдите в панель управления вашего проекта на Vercel

  2. Откройте вкладку Settings

  3. Выберите раздел Environment Variables

  4. Добавьте новую переменную:

  • Name: BOT_TOKEN

  • Value: ваш реальный токен бота

Не забудьте после этого переразвернуть (redeploy) проект, чтобы переменная стала доступна во время выполнения.


📤 4. Как поделиться из Telegram Mini App

  1. Фронтенд: пользователь нажимает «Поделиться» → отправьте данные на /api/share

  2. Бэкенд: вызовите savePreparedInlineMessage → получите ID сообщения

  3. Фронтенд: вызовите shareMessage({ id }), передав полученный ID сообщения

Frontend:

import { shareMessage } from '@telegram-apps/sdk-react';
// send info to the backend
const response = await shareTodo(todoId, userId, title, description);
if (response.ok) {
// get event id
  const data = await response.json();
  if (shareMessage.isAvailable()) {
// share message
    await shareMessage(data.id);
  }
}

Backend:

На вашем бэкенде получите данные и обратитесь к API-методу savePreparedInlineMessage для сохранения подготовленного сообщения.

const shareMessage = {
  user_id: userId,
  result: {
    type: 'photo',
    id: `todo-${todoId}`,
    photo_url: '<https://example.com/photo.jpg>',
    thumbnail_url: '<https://example.com/thumb.jpg>',
    title: `📝 ${todoTitle}`,
    description: todoDescription || 'A task from my Mini App',
    caption: `**${todoTitle}**\\n\\n${todoDescription || ''}`,
    parse_mode: 'Markdown',
  },
};
// send reuest to Telegram Bot API
const result = await savePreparedInlineMessage(shareMessage);
return NextResponse.json({ id: result.id });

📱 5. Некоторые компоненты Telegram SDK

🔙 Поведение кнопки «Назад»

Если у вашего Mini App несколько страниц или маршрутизация, возможно, вы захотите включить навигацию назад. По умолчанию кнопка «Назад» в Telegram не ведёт себя как кнопка браузера — её нужно явно инициализировать и контролировать.

На тех страницах, где должна быть видна кнопка «Назад», её нужно показывать; на остальных — скрывать. Иначе при нажатии кнопки приложение закроется, вместо того чтобы вернуться на предыдущую страницу.

❌ Поведение при закрытии

Если вы хотите напомнить пользователям сохранить данные перед закрытием приложения, можно включить подтверждение закрытия. Это помогает избежать случайной потери несохранённых изменений, запрашивая у пользователя подтверждение перед выходом.

🎨 Компонент Mini App

Чтобы настроить ваш Mini App — например, задать цвета заголовка или фона — или использовать методы для проверки статуса, такие как проверка, активен ли Mini App, сначала необходимо инициализировать (смонтировать) компонент Mini App. Только после монтирования можно безопасно вызывать эти методы.

'use client';

import { useEffect } from 'react';
import {
  backButton,
  closingBehavior,
  init,
  miniApp,
  useRawInitData,
} from '@telegram-apps/sdk-react';

export default function TelegramInit() {
  const rawInitdata = useRawInitData();

  useEffect(() => {
    try {
      init(); // Initialize Telegram SDK
      if (backButton.mount.isAvailable()) {
        backButton.mount();
        backButton.onClick(() => {
          if (backButton.isMounted()) {
            backButton.hide();
          }
          window.history.back();
        });
      }
      console.log('TelegramProvider - init()');
      if (miniApp.mountSync.isAvailable()) {
        miniApp.mountSync();
      }
      if (closingBehavior.mount.isAvailable()) {
        closingBehavior.mount();
        closingBehavior.isMounted(); // true
      }
      if (closingBehavior.enableConfirmation.isAvailable()) {
        closingBehavior.enableConfirmation();
        closingBehavior.isConfirmationEnabled(); // true
      }
    } catch (err) {
      console.error('Telegram SDK init failed:', err);
      const cleanUrl = window.location.origin + window.location.pathname;
      window.history.replaceState({}, '', cleanUrl);
    }

    return () => {
      if (backButton.isMounted()) {
        backButton.unmount();
      }
    };
  }, []);

💬 6. Telegram всплывающие окна

Если вкратце, вы создаёте всплывающее окно с помощью метода popup.show, указывая нужные кнопки.

Затем ждёте buttonId, который возвращает popup.show (то есть какую кнопку нажал пользователь), и выполняете действия в зависимости от выбранной кнопки.

import { popup } from '@telegram-apps/sdk-react';

export const showTelegramPopup = async (
  title: string,
  message: string,
  buttonText: string[]
): Promise<boolean> => {

  if (popup) {
    const promise = popup.show({
      title: title,
      message: message,
      buttons: buttonText.map((text, index) => ({
        id: `button_${index}`,
        type: 'default',
        text: text,
      })),
    });
    // popup.isOpened() -> true
    const buttonId = await promise;
    if (buttonId === 'button_0') {
      return true;
      // do something
    } else if (buttonId === 'button_1') {
      return false;
      // do something else
    }
    // If buttonId is neither 'ok' nor 'cancel', return false by default
    return false;
  }
  // If popup is not available, return false
  return false;
};

🔗 Ссылки

🔥 Анонс

Есть ещё много интересного — например, работа с платежами через Telegram Stars — но это не вошло в эту статью.

Если хотите, чтобы я осветила эту тему в следующий раз, поставьте лайк или оставьте комментарий ниже!

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.