LiveView con FastAPI | Programador Web Valencia

LiveView con FastAPI

4 minutos

FastAPI

He estado jugando con FastAPI para entender como de factible es crear soluciones sitios con un enfoque de HTML sobre WebSockets (LiveView). Me ha sorprendido en algunos aspectos, tanto para bien como para mal.

Empecemos con la demostración.

Se ha creado 2 páginas, una de inicio y otra de sobre nosotros. Se puede navegar entre ellas sin recargar la página. Además se ha creado un botón que cuando es pulsado muestra el tiempo actual en un elemento del DOM.

Código

Para el frontend, he utilizado un pack todo en uno: Django LiveView FrontEnd. Este paquete incluye la gestión de conexión, eventos y respuestas.

Puedes encontrar todo el código en el siguiente repositorio.

El código de FastAPI es el siguiente:

import os
import pathlib

from fastapi import FastAPI, Request, WebSocket
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

import home
import about_us

app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')

BASE_DIR = pathlib.Path(__file__).parent
templates = Jinja2Templates(
    directory=[
        BASE_DIR / 'templates',
    ]
)


@app.get('/')
async def welcome_page(request: Request):
    context = {
        'request': request,
    }
    return templates.TemplateResponse(
        'layouts/base.html', context,
    )



@app.websocket('/ws/liveview/')
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data_frontend = await websocket.receive_json()
        if data_frontend and 'action' in data_frontend:
            action_data = data_frontend['action'].split('->')
            if len(action_data) == 2:
                action = action_data[0].lower()
                function = action_data[1].lower()
                await eval(
                    f'{action}.{function}(websocket, templates, data_frontend)'
                )

En webcome_page se renderiza la página de inicio y en websocket_endpoint se manejan los eventos de LiveView.

La página base.html es sencilla.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Example</title>
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0, shrink-to-fit=no"
        >
	<!-- Django LiveView FrontEnd -->
	<!-- https://django-liveview.andros.dev/docs/ -->
	<script defer type="module" src="https://cdn.jsdelivr.net/gh/Django-LiveView/frontend/js/main.js"></script>
    </head>
    <body
        data-controller="page"
    >
        <header>
            <h1>FastAPI with LiveView</h1>
	    <nav>
		<ul>
		   <li>
		       <a
			   href="#"
			   data-action="click->page#changePage"
			   data-page="home"
			   role="button"
			>Home</a>
		   </li>
		   <li>
		       <a
			   href="#"
			   data-action="click->page#changePage"
			   data-page="about_us"
			   role="button"
		       >About Us</a>
		</ul>
	    </nav>
        </header>
        <main id="main"></main>
        <footer></footer>
    </body>
</html>

Puedes ver como he añadido 2 enlaces en el menú de navegación que cambian la página sin recargarla.

La página de home.html es tan solo texto estático.

<section>
    <h1>Home</h1>
    <p>
	In dictum non, consectetur a erat nam at lectus urna duis convallis convallis? Duis tristique sollicitudin nibh sit amet commodo nulla facilisi nullam vehicula ipsum a arcu cursus vitae congue mauris rhoncus aenean vel elit? Aliquet sagittis id consectetur purus. Pellentesque adipiscing commodo elit, at imperdiet dui accumsan sit. Praesent elementum facilisis leo, vel fringilla est ullamcorper eget nulla facilisi etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus in ornare quam viverra orci sagittis eu volutpat! Dui, id ornare arcu odio ut sem nulla pharetra diam sit amet nisl suscipit adipiscing bibendum est ultricies integer quis! Et leo duis ut diam quam nulla porttitor massa id neque! Morbi non arcu risus, quis varius quam quisque id diam vel quam elementum pulvinar etiam! Id faucibus nisl tincidunt eget nullam non nisi est, sit amet facilisis magna etiam tempor, orci eu lobortis elementum, nibh tellus molestie nunc, non blandit massa enim nec dui nunc! Cras ornare arcu dui vivamus arcu felis, bibendum ut tristique et, egestas quis ipsum suspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque dignissim! Commodo viverra maecenas accumsan, lacus vel facilisis volutpat, est velit egestas dui, id ornare arcu odio ut sem nulla pharetra diam.
    </p>
</section>

Aunque en about_us.html he añadido un botón que muestra la hora actual.

<section>
    <h1>About us</h1>
    <p>
	<button
	    data-action="click->page#run"
	    data-liveview-action="about_us"
	    data-liveview-function="current_time"
	>Get time</button>
    </p>
    <p id="time"></p>
</section>

Las funcionalidades de home se encuentra en home.py.

async def send_page(websocket, templates, data_frontend):
    await websocket.send_json(
        {
            'selector': '#main',
            'html': templates.get_template(
                f'pages/home.html'
            ).render(),
        }
    )

Y las de about_us en about_us.py.

from datetime import datetime

async def send_page(websocket, templates, data_frontend):
    await websocket.send_json(
        {
            'selector': '#main',
            'html': templates.get_template(
                f'pages/about_us.html'
            ).render(),
        }
    )

async def current_time(websocket, templates, data_frontend):
    await websocket.send_json(
        {
            'selector': '#time',
            'html': f"<datetime>{datetime.now()}</datetime>",
        }
    )

Cuando se pulsa el botón, se envía la etiqueta datetime con el output de datetime.now().

Y eso sería todo. En el respositorio podéis levantarlo para problarlo por vosotros mismos.

Puntos positivos

  • Facilidad a la hora de levantar un servidor de WebSockets: FastAPI es muy sencillo de usar y de configurar, tiene su propio paquete para manejar WebSockets.
  • Rápido de configurar y lanzar: En cuestión de minutos puedes tener una web levantada. Recuerda a Flask y similares.

Puntos negativos

  • No esta orientado a crear sitios HTML: Dependes de paquetes externos, como starlette para manejar las rutas y jinja2 para renderizar las plantillas. Además, si incluyes formularios, tendrás que hacerlo a mano o usar otros paquetes como wtforms.
  • No es posible el envío masivo o grupos de clientes: El servidor solo habla con el cliente conectado. No es posible enviar mensajes a todos los clientes o una selección concreta. Y si es posible hacerlo, no esta documentado.
  • No gestiona scopes, sesiones o cookies mediante WebSockets: No es posible guardar información del cliente en el servidor, como una sesión o un token. Tendrás que hacerlo a mano o usar otros paquetes como fastapi-sessions.

Conclusión

Tal vez el problema era que estaba metiendo con calzador un desarrollo web en un software que fue diseñado para realizar únicamente APIs. Después de la experiencia sigo opinando que Django, junto a Channels, es la opción más completa para realizar desarrollos web con WebSockets en el ecosistema de Python. El tiempo que ahorras en el inicio tiene un coste en la complejidad del desarrollo en el futuro.

Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-SinDerivadas 4.0 Internacional.

Atribución/Reconocimiento-NoComercial-SinDerivados 4.0 Internacional

¿Me ayudas?

Comprame un café
Pulsa sobre la imagen

No te sientas obligado a realizar una donación, pero cada aportación mantiene el sitio en activo logrando que continúe existiendo y sea accesible para otras personas. Además me motiva a crear nuevo contenido.

Comentarios

{{ comments.length }} comentarios

Nuevo comentario

Nueva replica  {{ formatEllipsisAuthor(replyComment.author) }}

Acepto la política de Protección de Datos.

Escribe el primer comentario

Tal vez también te interese...