EN RU

@demondehellis

Рассказываю о технологиях и программировании.

Backend без сервера

Как сделать веб-приложение, но при этом не возиться с сервером?

Для чего вообще нужен бэкенд или сервер? Большую часть сайтов-визиток, лендосов и корпоративных сайтов можно сделать на HTML. HTML можно загрузить в публичный бакет или в какой-нибудь GitHub Pages и просто отдавать статику. Кастомный бэкенд, а с ним и сервер, появляются тогда, когда возникает необходимость взаимодействия с вебом — оставить комментарий, загрузить фотографию, залогиниться, оплатить онлайн.

Большинство таких “фич” сводится всего к нескольким операциям:

Умные дяди из Гугла, Амазона и прочих облаков предлагают для этого уже готовые решения для разработчиков, на базе которых можно строить что угодно.

Clouds and Servers

🌥️ Облака

Облачная БД - это когда у тебя нет своего сервера с Mongo или MySQL, но есть API, с помощью которого ты можешь писать и читать данные из БД, которая располагается и обслуживается, например, Google.

Облачное файловое хранилище — это когда у тебя нет сервера с диском, на котором лежат файлы, но есть API, через который можно загружать и скачивать файлы с диска, который располагается и обслуживается тем же Google.

Облачные функции, лямбды и тому подобное — это когда у тебя нет сервера, на котором запущено твое приложение, а есть возможность загрузить свой скриптик на Python или на чем угодно в то же гугловое облако и запускать его по ссылке.

Ну, в общем, смысл, думаю, понятен: сервер как бы есть, но не у тебя. Соответственно, и проблемы, связанные с сервером, такие как безопасность, масштабирование, обновление и т.п., тоже есть, но не у тебя.

Все это в целом называется “облачная инфраструктура”, а инструменты и стек технологий, который с этим работают, называют serverless.

Что на бэкенде?

В моем случае — Flask. Он лёгкий, гибкий и почти не требует подготовки. Можно написать API, страницу, шаблон или просто отдавать markdown.

🔥 Пример на Flask

from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/api/hello")
def hello():
    return jsonify({"message": "Hello from Flask!"})

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

☁️ Flask в Cloud Functions

Flask легко заворачивается в облачные функции — я использую Cloud Functions for Firebase. Код кладётся в папку и деплоится одной командой.

from firebase_functions import https_fn
from flask import Flask

app = Flask(__name__)

@app.route('/') # Это route для Flask
def hello_world():
    return 'Hello from Firebase'

@https_fn.on_request() # А это обработчик для Firebase Functions
def main(req: https_fn.Request) -> https_fn.Response:
    # А тут мы прокидываем запрос во Flask
    with app.request_context(req.environ):
        return app.full_dispatch_request()

🧩 Jinja — шаблонизатор Flask

Еще одна фишка Flask — встроенный шаблонизатор Jinja. С его помощью легко разбивать HTML на подшаблоны, переиспользовать блоки и вставлять переменные.

<!-- layout.html -->
<html>
    <head>
        ...
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

Используем лэйаут в страницах:

<!-- page.html -->

{% extends "layout.html" %}

{% block content %}
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
{% endblock %}

Рендерим страницу:

from flask import render_template

@app.route('/page')
def page():
    return render_template('page.html', 
        title='Hello', 
        message='Welcome to Flask!'
    )

🍬 Flask + Flask-RESTX

Если API становится большим — удобно подключить Flask-RESTX. Он позволяет быстро описывать ресурсы, маршруты и типы.

from flask_restx import Api, Resource, fields, marshal_with

# Определяем модель
UserPublic = api.model('User', {
    'id': fields.Integer,
    'name': fields.String,
    'email': fields.String
})

@api.route('/user')
class UserResource(Resource):
    @marshal_with(UserPublic) # Декоратор для сериализации по модели
    def get(self):
        # Возвращаем словарь или объект
        return {
            'id': 1,
            'name': 'John Doe',
            'email': 'john@example.com',
            'password': 'secret123',  # Это поле не попадет в ответ
            'internal_data': 'hidden'
        }
    

Из полезного у Flask-RESTX:

Ну и почти что угодно еще, что нужно для создания RESTful API.

🔥 Firestore

Firestore — облачная NoSQL БД от Firebase. Легко интегрируется с Flask и позволяет хранить данные в виде документов и коллекций.

from firebase_admin import firestore

@app.route('/api/articles', methods=['GET'])
def get_articles():
    # Подключаем Firestore
    db = firestore.client() 
    
    # Получаем ссылку на коллекцию "articles"
    articles_ref = db.collection('articles') 
    
    # Получаем итератор документов
    docs = articles_ref.stream() 
    
    # Переводим в JSON
    return jsonify([doc.to_dict() for doc in docs]) 

Или, вот, например, запись:

from firebase_admin import firestore

@app.route('/api/articles', methods=['POST'])
def create_article():
    db = firestore.client()
    articles_ref = db.collection('articles')
    new_doc_ref = articles_ref.add(request.json)
    
    return jsonify({"id": new_doc_ref.id}), 201

Schema не нужна, данные могут быть любыми. Но можно использовать правила безопасности для контроля доступа:

service cloud.firestore {
  match /databases/{database}/documents {
    
    match /articles/{articleId} {
      allow read, write: if request.auth != null;
    }
  }
}

В том числе есть правила для валидации данных: тип, размер, обязательность полей, regex-проверки и т.д.

Только Python?

Нет, конечно. Firebase поддерживает и Node.js, но мне привычнее на Python. У других облачных провайдеров можно найти и Go, и PHP, и много чего другого.

Кроме того, у Гугла есть Cloud Run — сервис для запуска контейнеров. Можно упаковать приложение в Docker и запускать его в облаке. В этом случае можно использовать вообще что угодно, что работает в Докере. Можно и просто на баше запускать скрипты и консольные приложения. В отличие от Cloud Functions, где код запускается по событию и живет ограниченное время, в Cloud Run приложение может работать постоянно, как на обычном сервере.

Почему Serverless?

Мне кажется, мы все туда в итоге придём. Осталось дождаться прорыва — возможно, появится что-то вроде “Laravel для serverless”, и все там будем.

Преимущества очевидны:

Из недостатков в основном только непривычная архитектура и вендорлок. Ну и на данном этапе отсутствие высокоуровневых фреймворков, CMS и т.п.

Собственные серваки, как мне кажется, останутся только для специфичных задач, вроде стриминга, игр или чего-то, что требует постоянного соединения.


Еще всякое интересное