Esploriamo da vicino il mondo delle API – Application Programming Interfaces
Scopriamo Litestar, un framework che sta conquistando ampi consensi
Litestar è un framework basato su Python che punta a rendere estremamente semplice lo sviluppo di web APIs.
Abbiamo parlato approfonditamente delle API nei nostri precedenti articoli, sia dal punto di vista della teoria che della pratica, coprendo la totalità dei framework Python più utilizzati.
Partendo da queste premesse e proseguendo il nostro arco narrativo, oggi introduciamo Litestar.
Litestar API framework: un po’ di storia
Litestar è un framework di relativamente recente genesi (l’alpha ha visto la luce nel 2010, sebbene la sua diffusione sia schizzata solo con il rilascio della stable 2.0 che risale all’Agosto 2023).
Litestar era precedentemente chiamato Starlite, un nome scelto come omaggio a Starlette, il framework ASGI su cui Starlite era inizialmente basato. Nel corso del suo sviluppo, Starlite è diventato sempre più indipendente e ha fatto sempre meno affidamento su Starlette, fino al punto in cui Starlette è stato ufficialmente rimosso come dipendenza nel novembre 2022, con il rilascio della versione 1.39.0. A quel punto è avvenuto anche un rebranding che ha portato a coniare il nome definitivo, Litestar. A oggi esistono diversi conflitti a livello SEO per via della sovrapposizione con un programma di illuminotecnica che si sovrappone pesantemente nella search. Dettagli che sarebbe bene considerare quando si effettua un rebranding di un prodotto tecnologico!
A differenza della logica seguita da framework full-stack come Django, Litestar punta a un approccio “no strings attached”, ossia fornendo un ambiente immediatamente funzionale, ma anche scevro da orpelli, vincoli e complessità superflue nelle fasi implementative.
Questo permette di avere performance ottimizzate e un repository agile e rapido anche per lavorare celermente su bug fixing e issues.
Litestar è un prodotto open source gestito su base volontaria dalla Litestar Organization. Il repository ufficiale di Litestar ha oltre 3.2K star su GitHub.
La filosofia del progetto
- Litestar è un progetto guidato dalla comunità. Ciò significa che non è guidato da un singolo autore, ma piuttosto da un team principale di manutentori, supportato da una comunità di contributori.
- Litestar si ispira a NestJS, un framework TypeScript per costruire applicativi server-side efficienti e scalabili.
- Pur consentendo l’uso di endpoint basati su funzioni, Litestar ambisce a sfruttare il paradigma della programmazione orientata agli oggetti di Python, ponendo al centro i controller basati su classi.
- Non chiamatelo micro framework. A differenza di framework come FastAPI, Starlette o Flask, Litestar include molte funzionalità già integrate necessarie per un’applicazione web moderna tipica, come l’integrazione ORM, sessioni lato client e lato server, caching, integrazione di OpenTelemetry e molte altre. Pur non puntando a essere un Django-clone, non va neppure considerato “micro”.
Le funzionalità principali di Litestar
Litestar offre un discreto numero di funzionalità out-of-the-box, il che lo rende immediatamente comparabile con framework superstar quali FastAPI.
Litestar include:
- Supporto OpenAPI.
- Documentazione automatica con Swagger, ReDoc, Stoplight.
- Data Validation.
- Dependancy Injection.
- Class-based routing.
- ORM integration con SQLAlchemy, Tortoise e Piccolo.
- Templating con il celebre Jinja e Mako.
- MessagePack.
- CORS (Cross-Origin Resource Sharing).
- CSRF (Cross-Site Request Forgery).
- Rate-limiting.
- JWT (JSON Web Token).
- Sessions and Auth handling.
- Caching.
- Telemetry.
- Middleware customizzati.
- WebSocket per comunicazione in tempo reale.
Creaiamo una API con Litestar
Dopo questa introduzione, possiamo accingerci a creare la nostra web API.
L’esempio di quest’articolo è stato creato seguendo puntualmente le best practice del framework suggerite nella guida d’uso.
Hello, World
Per prima cosa, proviamo l’Hello World con Starlite.
Installiamo il framework con l’inclusione del web server uvicorn:
c:\User\user\my_venv>pip install litestar[standard]
poi creiamo un basico routing sulla home page del nostro localhost:
from litestar import Litestar, get @get("/") async def hello_world() -> str: return "Hello, world!" app = Litestar([hello_world])
Notiamo subito una caratteristica importante: il type annotation (anche: type hint). L’output della nostra funzione in home page sarà una stringa, certamente. Allora indichiamola esplicitamente con l’annotazione -> str:
. Anche se come noto le annotazioni non influenzano il runtime degli script, in Litestar sono usate per convalidare i dati in ingresso per ciascuna request.
Se Litestar è associato a strumenti di convalida come mypy o pyright, è possibile rendere il codice più sicuro e robusto in modo organico e nativo.
Importando la classe Litestar possiamo definire il routing della homepage semplicemente invocando il decoratore seguito dall’url: @get("/")
. Questo risulterà molto familiare a chi già proviene da Flask, ad esempio.
Se si è seguito il processo passo-passo, possiamo lanciare direttamente il nostro webserver all’interno dell’ambiente di sviluppo tramite Uvicorn.
Il comando litestar run
ci permette di avviare un’istanza di un webserver in locale per interagire con la nostra applicazione.
Una semplice TODO LIST
Abbiamo già rotto il ghiaccio con Litestar. Proviamo ad aggiungere ancora qualcosa.
from litestar import Litestar, get TODO_LIST: list[dict[str, str | bool]] = [ {"title": "Start writing TODO list", "done": True}, {"title": "???", "done": False}, {"title": "Profit", "done": False}, ] @get("/") async def get_list() -> list[dict[str, str | bool]]: return TODO_LIST app = Litestar([get_list])
Lanciamo nuovamente l’app e notiamo subito la potenza del framework, che ci renderizza i dati già pronti in schema:
Ma come è possibile?
Il segreto, per così dire, è celato nell’annotazione passata alla funzione get_list
. Grazie all’annotazione List[Dict[str, Union[str, bool]]]
, Litestar comprende che si richiede di esporre un dato serializzato come JSON.
Siccome Litestar vuole portare il programmatore a ragionare in un contesto orientato agli oggetti, è possibile anche ottenere lo stesso risultato mediante una classe:
from dataclasses import dataclass TODO_LIST: list[TodoItem] = [ TodoItem(title="Start writing TODO list", done=True), TodoItem(title="???", done=False), TodoItem(title="Profit", done=False), ]
Proviamo ora a implementare una semplice logica querystring per ritornare tramite una API call gli oggetti nella TODO list che sono stati smarcati (1) o sono ancora da affrontare (0). Per sicurezza, inseriamo anche la gestione di un errore in caso venga passato all’API un parametro non riconosciuto. Si può notare come la logica seguita dal framework sia perfettamente in sintonia con prodotti analoghi, rendendo la curva di apprendimento davvero molto smooth.
@get("/") async def get_list(done: str) -> list[TodoItem]: if done == "1": return [item for item in TODO_LIST if item.done] if done == "0": return [item for item in TODO_LIST if not item.done] raise HTTPException(f"Invalid query parameter value: {done!r}", status_code=400)
Volendo seguire una logica più elegante, possiamo validare la tipologia di parametro, invece che i singoli parametri:
@get("/") async def get_list(done: bool) -> list[TodoItem]: return [item for item in TODO_LIST if item.done == done]
Qui vediamo all’opera la potenza del framework usato in comunione con i type hints. Litestar tenta di convertire il parametro passato in booleano; se questo non è possibile, ritorna un ValidationError
. La logica seguita da Litestar è lievemente più sofisticata rispetto a quella propria del linguaggio Python; sappiamo che in Python bool("Pippo")
ritorna True, in quanto ogni stringa non vuota è True-ish
. Ma Litestar si affida alla logica booleana comunemente usata in HTTP, pertanto termini non propri di questa classificazione vengono consideranti False
.
Documentazione pronta all’uso
Grazie all’integrazione nativa con strumenti di documentazione come Swagger, ReDoc e Stoplight, possiamo navigare nella nostra API con estrema semplicità e con il supporto di una comoda GUI. Ideale per la condivisione dell’API con il team di sviluppo e con i nostri partner tecnologici.
Visitiamo quindi l’URL dedicato: http://127.0.0.1:8000/schema/swagger e ci troveremo in questo contesto:
Da GET a POST in poche righe di codice
Finiamo la nostra esplorazione aggiungendo anche la possibilità di inserire nuovi item nella nostra lista.
from typing import Any from litestar import Litestar, post @post("/") async def add_item(data: dict[str, Any]) -> list[dict[str, str | bool]]: TODO_LIST.append(data) return TODO_LIST
Nel framework Litestar, i dati della richiesta possono essere ricevuti utilizzando la parola chiave data
. Litestar riconoscerà questo parametro e fornirà i dati inviati con la richiesta attraverso questo comando.
Come per i parametri di query visti precedentemente, utilizziamo le annotazioni di tipo per configurare quale tipo di dati ci aspettiamo di ricevere e impostare la convalida.
In questo caso, Litestar si aspetterà i dati della richiesta nel formato JSON e utilizzerà l’annotazione di tipo che abbiamo fornito per convertirli nel formato corretto.
Possiamo interagire con l’applicazione nuovamente invocando Swagger all’ormai usuale URL http://127.0.0.1:8000/schema/swagger.
Litestar: un’arma in più per la creazione di API sicure e efficaci
Non crediamo che Litestar abbia bisogno di particolare endorsement, e senza dubbio si trova circondato da validissimi concorrenti quali i già citati FastAPI, Flask, Django. Abbiamo però appreso i rudimenti di un nuovo strumento e aumentato la nostra conoscenza in materia di web API e strumenti di progettazione rapida e dinamici. Non male no?
Se vuoi scoprire di più sull’universo delle API, vorresti sviluppare API per il tuo applicativo, o integrare servizi tramite API di terze parti, contattaci!