How to make full-stack from one device without SMS and registration

This article is written for lamers

I was learning to code as usual, and suddenly noticed that telegram released a web api and now there is a front…

At that point, I already owned React, Flask, and Django, and decided to raise the bar with FastAPI.

Believing in myself, I installed FastAPI and aiogram. Obviously, I should have used the native methods of the Telegram API, but aiogram provides many handy features that speed up development.

Problem

Django and Flask use templating engines (I used Jinja2), which means that the code that runs in the browser (HTML, JS, CSS, TS, etc.) is sent directly from the server. Thus, such an application does not have a separate server for the frontend.

however Fast-api and React need separate servers…

I understand that there will be people who will say: “Why use a separate server for React when you can add reactivity with Knockout.js and build a web application on a single server?” – Yes, it is possible, but it seems to me that it is better to use normal technologies, and not crutches on crutches, so that by the time the project is completed you have valuable experience in your resume, and not a reason for retirement))

I’ve come across enterprise projects written using legacy Knockout, js and Python 2, all on the same monolith. The scheme works, besides, you can hire juniors and pay them less, because everyone is taught Django in courses).

Solution

Here we have two servers – FastAPI and React. Next, we need to connect them with the Telegram API. Consider a specific example of such a connection: of course, FastAPI and Telegram interact via a webhook. To bring them together, we send a Telegram request to the server, in which we indicate the public address received from ngrok and the bot token from the .env file. In my case, I added the address of ngrok to .env and FastAPI automatically connects to Telegram on startup. The main problem is that ngrok, even in the paid version, only supports one address, while my frontend runs on a separate port. However, localhost.run comes to the rescue here. It has similar functionality to ngrok, but is a bit more configurable.

Here is the plan I would follow

  1. Run localhost.run on port 3000 and add the address to the backend environment

  2. run React on port 3000

  3. Run ngrok on port 8000 and add the address to the environment

  4. Run Fast-api locally on port 8000

The plan is simple and reliable, like a Swiss watch.

1.localhost.run

We go to https://admin.localhost.run/ sign up/log in.

Making shh key orig. instruction in English. The key consists of 2 parts public and private. go to the terminal and write

ssh-keygen -t rsa -b 2048 -C "<comment>"

*It is recommended to include an email address in the comments

we get this as an answer

we get this as an answer

choose the name of the key. and remember the path. if you do not enter a name, then the keys will be generated with the default names id_rsa and id_rsa.pub

press enter, we get a message that the key is ready.

press enter, we get a message that the key is ready.

go to the directory of the key, press ls -1 to make sure that we came to the right place and the keys are ready and waiting for us.

ls -1
as I understand it, you can only get into the .ssh folder through the console

as I understand it, you can only get into the .ssh folder through the console

id_rsa and id_rsa.pub are private and public keys respectively. Open id_rsa.pub with vim or nano. If there is no vim, then install vim))

vim id_rsa.pub

copy everything inside (starting with shh and up to the end of the text)
then exit vim to do this, press “:” and type q! and press enter.

go to browser https://admin.localhost.run/#/keys enter this key

click submit

click submit

then go to the console

ssh -R 80:localhost:3000 localhost.run

What does this command mean can be read in the service documentation

We get our white address on port 3000

on the screen port 3003 and not 3000

on the screen port 3003 and not 3000

add the received address to the backend .env

now it’s time for REACT

2. REACT

So in order to do without dancing, I made a wizzard script. I note that it allows you to choose js / jsx, yarn / npm, and creates a docker and docker compose with forwarded ports, and all code is moved to Valium, so the project does not require constant rebuilding.

So, create create_react_compose_app.sh or download from github.

We make it executable, on a poppy it is done like this:

chmod +x create_react_compose_app.sh

Let’s run the script:

bash create_react_copmpose_app.sh
then we select the main parameters, everything is done so that you can click and use the default parameters.  project name is pwd + web re , npm , js

then we select the main parameters, everything is done so that you can click and use the default parameters. project name is pwd + web re , npm , js

checking the dockerfile

checking the dockerfile

Run docker, then enter in the console

docker-compose up

And now the application is open on port 3000, now the browser goes to the address that we received in localhost.run

and here we are online

and here we are online

Let’s go to

go to

go to

Open index.html and add the telegram library there

<script src="https://telegram.org/js/telegram-web-app.js"></script>
straight to the head

straight to the head

And replace

<p>  Edit <code>src/App.js</code> and save to reload.</p>
  <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>

On

<p> Hellow Habr. </p>

In src/App.js

check at the address given by localhost.run

everything works, good luck :)

everything works, good luck 🙂

3. Ngrok

go to browser https://ngrok.com/ Registering / logging in

download the version for your OS

download the version for your OS

go to https://dashboard.ngrok.com/get-started/setup

do everything according to the instructions:

  • unzip zip

  • substitute the key in the config, it is convenient that the documentation immediately gives personal commands with the substituted key

    no need to substitute anything, this is a ready-made command.  I will leave here a link to the ngrok documentation
  • run ngrok on port 8000

    ngrok http 8000
and get the second white api address

and get the second white api address

4. Fast-API

go to source folder

go to the arrow

go to the arrow

create a Fast-API folder using the command

mkdir Fast-API

let’s get into it

cd Fast-API

create main.py And README.md, .env using the familiar touch command. It’s easy to remember about not having a song Daft Punk

create a file and put the address from localhost.run , ngrok , tokenbot …

touch .env
here is an example syntax for .env file

here is an example syntax for .env file

Create venv

Yes, normal boys use Poetrybut this text is exclusively for lamers, if you are ready to do it right away, then go ahead, it will be through venv.

python3 -m venv venv 

activate it

source venv/bin/activate

update

pip install --upgrade pip

put uvicorn

pip install uvicorn

set fast-api

pip install fastapi

set aiogram

pip install aiogram

install python-dotenv, this is one of those packages that are not named in pip as they are imported

pip install python-dotenv

V README.md write start command

$ uvicorn main:app --reload

open main.py

write imports and load variables from .env

from dotenv import load_dotenv
from fastapi import FastAPI

load_dotenv() # загружем .env в оперативку

TOKEN = os.environ["TELEGRAM_TOKEN"] # получаем занчения переменных
BACK_URL = os.environ["BACK_URL"]
REACT_URL = os.environ["REACT_URL"]

WEBHOOK_PATH = f"/bot/{TOKEN}" # формируем системные переменные из переменных окружения
WEBHOOK_URL = BACK_URL + WEBHOOK_PATH

create an application and write a check function

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello habr"}

start the uvicorn server with the application app = FastAPI with the command from README.md

 uvicorn main:app --reload

go to BACK_URL in the browser, check that everything is working and we correctly installed and imported the .env file and launched the FastAPI correctly

everything works for me, lucky))

everything works for me, lucky))

import aiogram and create message handler functions And so on

@app.on_event("startup") #обработчик запуска приложения, засылает вебхук в телеграм
async def on_startup():
    webhook_info = await bot.get_webhook_info()
    if webhook_info.url != WEBHOOK_URL:
        await bot.set_webhook(
            url=WEBHOOK_URL
        )


@app.post(WEBHOOK_PATH) #обработчик событий телгарма 
async def bot_webhook(update: dict):
    telegram_update = types.Update(**update)
    Dispatcher.set_current(dp)
    Bot.set_current(bot)
    await dp.process_update(telegram_update)

add an old command handler in telegram

@dp.message_handler(commands="start")
async def new_message(message: types.Message):
    text="REACT"
    keyboard = types.InlineKeyboardMarkup()
    keyboard.add(types.InlineKeyboardButton('Launch react', web_app=WebAppInfo(url=REACT_URL)))
    await bot.send_message(message.chat.id, text, reply_markup=keyboard)


@app.on_event("shutdown") #и обработчик закрытия сесии 
async def on_shutdown():
    await bot.get_session()
    await bot.session.close()
    logging.info("Bot stopped")

add favicon.iso and here is the full code

import logging
import os

from aiogram import types, Dispatcher, Bot
from aiogram.types import WebAppInfo
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.responses import FileResponse

load_dotenv()

TOKEN = os.environ["TELEGRAM_TOKEN"]
BACK_URL = os.environ["BACK_URL"]
REACT_URL = os.environ["REACT_URL"]

WEBHOOK_PATH = f"/bot/{TOKEN}"
WEBHOOK_URL = BACK_URL + WEBHOOK_PATH

bot = Bot(token=TOKEN)
dp = Dispatcher(bot)

app = FastAPI()

favicon_path="favicon.ico"


@app.get('/favicon.ico', include_in_schema=False)
async def favicon():
    return FileResponse(favicon_path)


@app.get("/")
async def root():
    return {"message": "Hello habr"}


@app.on_event("startup")
async def on_startup():
    webhook_info = await bot.get_webhook_info()
    if webhook_info.url != WEBHOOK_URL:
        await bot.set_webhook(
            url=WEBHOOK_URL
        )


@app.post(WEBHOOK_PATH)
async def bot_webhook(update: dict):
    telegram_update = types.Update(**update)
    Dispatcher.set_current(dp)
    Bot.set_current(bot)
    await dp.process_update(telegram_update)


@app.on_event("shutdown")
async def on_shutdown():
    await bot.get_session()
    await bot.session.close()
    logging.info("Bot stopped")


@dp.message_handler(commands="start")
async def new_message(message: types.Message):
    text="REACT"
    keyboard = types.InlineKeyboardMarkup()
    keyboard.add(types.InlineKeyboardButton('Launch react', web_app=WebAppInfo(url=REACT_URL)))
    await bot.send_message(message.chat.id, text, reply_markup=keyboard)


@app.post(WEBHOOK_PATH)
async def bot_webhook(update: dict):
    telegram_update = types.Update(**update)
    Dispatcher.set_current(dp)
    Bot.set_current(bot)
    await dp.process_update(telegram_update)

save, run, check

I coo in general)

I coo in general)

In this article, I described setting up a web application in the context of #Telegram and #Web Apps for Bots. If desired, you can also enable the use of the FAST-API in the container using #poetry, but this is no longer for beginners. In addition, we also touched on #ssh keys, #react, #python, #fast-api, #docker, #docker-compose, #ngrok, #localhost.run, #venv, #uvicorn and used #shell-script.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *