Quarto articolo di approfondimento sul mondo delle API – Application Programming Interfaces
Costruiamo un’API RESTful con FastAPI, un framework Python che sta avendo una grande diffusione
La puntata di oggi è dedicata alla costruzione di una API RESTful da zero, utilizzando il framework Python FastAPI.
Ma prima di partire, facciamo un rapido riassunto del viaggio che ci ha portato hands-on alla scoperta delle API – Application Programming Interfaces.
- Abbiamo prima di tutto approfondito la parte teorica: API, cosa sono e come funzionano.
- Poi abbiamo costruito una web API da zero con Flask: Sviluppiamo una Web API con Python, Flask e SQLite.
- E con Litestar: Sviluppiamo una Web API con Litestar.
- E nella puntata precedente abbiamo invece costruito una REST API con Django Rest Framework.
Oggi ci occuperemo della creazione di una API RESTful e lo faremo usando FastAPI. Per prima cosa quindi scopriamo le caratteristiche di questo framework Python.
FastAPI: cos’è
FastAPI è definito come un framework ad elevate performance pronto per l’utilizzo in produzione.
FastAPI è stato rilasciato nel 2018, parliamo quindi di un framework recente, in senso assoluto ma anche se comparato a Django (2005) o Flask (2010).
Sebbene si sottolinei che FastAPI sia stato possibile grazie ai contributi della community , il suo ideatore è stato Sebastián Ramírez Montaño.
FastAPI è realmente “fast”, non solo nel nome. Secondo i benchmark è infatti oggi uno dei framework Python più veloci, in terza posizione dopo Uvicorn e Starlette (entrambi utilizzati da FastAPI stesso).
Caratteristiche principali
FastAPI nasce come un framework asincrono (ASGI – Asynchronous Server Gateway interface), in contrapposizione a Flask e Django ad esempio, nativamente sincroni (WASGI – Web Server Gateway Interface). Questo aspetto spiega largamente il motivo delle performance nettamente superiori rispetto ai suoi predecessori, comparabili a Node o Go.
Inoltre grazie all’uso di Pydantic, FastAPI permette la definizione esplicita (type hints) della tipologia di dati al momento del runtime (riducendo l’incidenza dei bug di circa il 40%), e permettendo anche check automatici tramite IDE e plugin in esso integrabili, evitando tediosi check in fase di debugging.
FastAPI è definito un framework moderno. Questo significa che si rifà a best practice e conoscenze di uso comune nella industry e non richiede quindi un flusso di apprendimento specifico.
FastAPI è basato su open standards, ossia linee guida che permettono di mantenere la tecnologia accessibile, aperta e trasparente.
FastAPI permette anche la generazione automatica di documentazione, grazie a SwaggerUI e ReDocs.
La popolarità di FastAPI è in costante crescita. Il repository GitHub conta oltre 3.9K fork e circa 50K stelle. É utilizzato da società come Microsoft, Uber, Nexflix.
FastAPI vs Flask
Per via dei casi d’uso similari e della struttura dei due framework, FastAPI è spesso indicato come un’alternativa a Flask.
La risposta non può che provenire dai singoli casi d’uso, ma in linea generale possiamo delineare le caratteristiche principali dei due framework.
Flask
- Maggiore flessibilità.
- Ampia community e referenze disponibili.
- Focus sulla sicurezza con integrazioni consolidate come Flask-Security.
- Facilmente scalabile.
FastAPI
- Performance eccezionali.
- Nativamente asincrono.
- Rispondente ai più aggiornati standard e best practice.
- Evoluzione recente e in via di definizione.
Costruiamo la nostra API RESTful con FastAPI
Chiarite queste premesse, mettiamoci ora al lavoro.
Andremo a realizzare un’API RESTful che permetterà di accedere e interagire con un dataset minimale creato contestualmente alla nostra applicazione.
Questa volta parleremo di videogame.
Qui un assaggio del prodotto finito:
Se non hai neppure voglia di mettere mano al codice ma vuoi provare comunque l’applicativo, ne ho creato una versione interattiva su Replit.com.
La trovi qui: https://replit.com/@carlo_/fastapitest#main.py
Ma ora partiamo con il tutorial passo-passo.
Creazione dell’ambiente
Come usuale, per prima cosa crea un ambiente virtuale:
python3 -m venv my_env
Attiva poi l’ambiente virtuale creato (segui le istruzioni in base al tuo sistema operativo) e dopodiché installa le dipendenze necessarie
pip install wheel
pip install fastapi[all]
In alternativa puoi anche clonare direttamente il repository di questo tutorial:
git clone https://github.com/carloocchiena/fastAPI_RESTful
e installare tutte le dipendenze direttamente dal file requirements.txt
.
pip install -r requirements.txt
Import delle librerie necessarie
A questo punto creiamo un file .py e iniziamo la costruzione della nostra API.
Per prima cosa importiamo le librerie necessarie
from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional import uvicorn
Creazione del dataset
In modo altrettanto lineare e direi auto esplicativo, instanziamo la nostra app, creiamo il dataset, e definiamo la tipizzazione dei singoli elementi.
# instantiate our FastApi application app = FastAPI() # initiate our test dataset videogames = [ {'title': 'Doom Eternal', 'rating': 4, 'cost': 45}, {'title': 'Days Gone', 'rating': 1, 'cost': 25}, {'title': 'The Last Of Us', 'rating': 3, 'cost': 75}, {'title': 'Detroit:Become Human', 'rating': 5, 'cost': 30}, {'title': 'Cyberpunk 2077', 'rating': 2, 'cost': 5}, ] # define the types of our dataset objects class Videogame(BaseModel): title: str rating: int cost: int
Creiamo una funzione di controllo
Andremo molte volte a chiamare i nostri oggetti tramite API e vogliamo essere certi che in caso di errore ci venga ritornato uno stato 404. Possiamo agilmente fare ciò con una funzione. Ci assicuriamo che l’oggetto identificato dall’ID proposto sia esistente; se così non è, chiediamo venga ritornato un messaggio di errore.
# check existance of the videogame def videogame_check(videogame_id): if not videogames[videogame_id]: raise HTTPException(status_code=404, detail='Game Not Found')
Un primo test
Anche se non strettamente necessario ai fini della nostra API, aggiungiamo un percorso per la home page, così da avere un messaggio appena lanciamo la nostra applicazione. Questo ci aiuta a prendere confidenza con la sintassi di FastAPI.
Notiamo da un parte la similitudine con Flask, dall’altra l’utilizzo di una funzione asincrona (in questo caso a scopo meramente illustrativo).
# create our home page route @app.get('/') async def root(): return {'message': 'Hello world'}
Inoltre, dopo il consueto check “if __name__ == ‘__main__'”, che ci permette di eseguire l’applicazione solo se invocata nativamente, e non importata, lanciamo il nostro script.
# launch our application with uvicorn if __name__ == '__main__': uvicorn.run(app,host="0.0.0.0",port="8080")
Giunti fin qui siamo in grado di fare un primo run.
Se tutto funziona, collegandoci al nostro localhost alla porta indicata (http://127.0.0.1:8080), vedremo comparire il messaggio di saluto.
Creiamo i metodi della nostra API
Addentriamoci ora nel vivo della nostra RESTful API, aggiungendo i metodi GET, POST, PUT, DELETE.
GET
Potremmo creare una semplicissima funzione GET, ma già che ci siamo aggiungiamo un pizzico di sapore integrando con un URL parametrizzato che consente di filtrare i videogiochi in base al loro ranking.
Questo ci permette di scoprire sia la sintassi da utilizzare per inserire parametri aggiuntivi, sia la loro opzionalità con il prefisso Optional
.
La funzione è parlante: posso inserire nella mia chiamata un valore di max e min rating e, se tali valori sono entrambi valorizzati, avrò in ritorno gli oggetti così selezionati.
Per semplicità, consideriamo sia massimo che minimo senza gestire il caso in cui sia fornito solo uno dei due estremi.
# GET @app.get('/videogames') def videogame_list(min_rate:Optional[int]=None, max_rate:Optional[int]=None): if min_rate and max_rate: filtered_videogames = list( filter(lambda rating: (min_rate <= rating['rating'] <= max_rate), videogames) ) return {'videogames': filtered_videogames} return {'videogames': videogames}
Voglio poi aggiungere un ulteriore metodo che mi permetta di chiamare direttamente un oggetto nella lista, tramite il suo identificativo. Come abbiamo visto in precedenza, vogliamo che se l’oggetto non esiste, ci venga ritornato un errore, pertanto invochiamo anche la nostra funzione di check.
# check if a videogames is within the list @app.get('/videogames/{videogame_id}') def videogame_detail(videogame_id: int): videogame_check(videogame_id) return {'videogames': videogames[videogame_id]}
A questo punto, possiamo testare questa parte, ad esempio con una chiamata all’URL:
http://127.0.0.1:8080/videogames?min_rate=1&max_rate=2
Avremo indietro i seguenti oggetti:
{"videogames":[{"title":"Days Gone","rating":1,"cost":25},{"title":"Cyberpunk 2077","rating":2,"cost":5}]}
POST
Possiamo dire che giunti fino qui, il resto procede praticamente in automatico. Aggiungiamo una chiamata POST che consenta di aggiungere un videogioco alla nostra lista e ci ritorni proprio l’oggetto aggiunto (ossia l’ultimo della lista).
Come intuibile si segue la stessa struttura precedente, anzi, qui addirittura semplificata.
# POST @app.post('/videogames') def videogame_add(videogame: Videogame): videogames.append(videogame) return {'videogames': videogames[-1]}
PUT
Poi aggiungiamo un metodo PUT per modificare invece il nostro dataset.
# PUT @app.put('/videogames') def videogame_update(videogame: Videogame, videogame_id: int): videogame_check(videogame_id) videogames[videogame_id].update(videogame) return {'videogames': videogames[videogame_id]}
DELETE
E infine il metodo delete. Anche qui, tutto lineare:
# DELETE @app.delete('/videogames') def videogame_delete(videogame_id: int): videogame_check(videogame_id) del videogames[videogame_id] return {'videogames': videogames}
Il prodotto finito: la nostra RESTful API
Abbiamo creato la nostra RESTful API con FastAPI in poco meno di cento righe di codice. Potremmo già essere soddisfatti così, ma la parte più bella invece deve ancora arrivare.
Proprio per quanto specificato nelle premesse, con FastAPI la documentazione della nostra API ci viene fornita inclusa nel prezzo.
Ecco che quindi chiamando l’endpoint di SwaggerUI otteniamo una dashboard interattiva che permette anche di testare tutti i singoli elementi fin qui creati, con tanto di istruzioni.
Buttiamoci quindi su http://127.0.0.1:8080/docs per ottenere questa dashboard interattiva:
Il riassunto del nostro viaggio
Giunti fin qui con successo, abbiamo scoperto cosa sia FastAPI, come permetta di creare una API RESTful, e quali siano le caratteristiche principali di questo moderno framework. Non male!
Per approfondire, qui trovi la documentazione ufficiale di FastAPI.