Diseño de API REST bien estructuradas con Flask-RestPlus: Parte 1

Foto: Simone Viani @ Unsplash

Nota: Este artículo pronto estará disponible únicamente en mi blog. Antes de moverlo de Medium, me gustaría pedirles a todos que escriban más comentarios y solicitudes de funciones allí. ¡Gracias!

Esta es la primera parte de una serie de dos partes. En esta publicación, presentaré Flask-RestPlus y demostraré cómo comenzar a organizar las API en torno a sus convenciones simples basadas en REST. La próxima vez, abordaré el tema de la solicitud / respuesta de clasificación (serialización) y validación.

Como desarrollador experimentado de Spring, me sentí un poco incómodo al diseñar y preparar el futuro de una API basada en Flask por primera vez. Recientemente comencé a usar Python mucho más allá de mi intención original de solo jugar con datos, y descubrí que Flask es una alternativa de microservicio súper fácil para Spring Boot o Ktor. Lo único que realmente me preocupaba era asegurarme de que el formato de solicitud / respuesta de la API estuviera estandarizado (piense, proporcionando un esquema Swagger), bien documentado y validado. Mientras trabaja con Java, gran parte de esto vendría directamente del compilador, debido a la naturaleza de tipo estático del lenguaje. Cuando combina esto con un par de excelentes bibliotecas como Jackson y SpringFox, la comunicación API se documenta y valida con una intrusión mínima en el código real. En Python, esto requeriría tediosas verificaciones de si-todo en todo el lugar ... o eso pensé.

Flask-RestPlus al rescate

A diferencia de Django, Flask no viene con baterías incluidas, pero existe un ecosistema completo de bibliotecas de código abierto y extensiones aportadas por la comunidad. Uno de ellos se llama Flask-RestPlus y es el sueño hecho realidad para todos los diseñadores de API de Flask. Flask-RestPlus es una biblioteca de extensiones de Flask y, como su nombre lo indica, ayuda a facilitar la construcción de API RESTful estructuradas con una configuración mínima y fomenta las mejores prácticas. Flask RestPlus sigue ciertas convenciones, pero no insiste en ellas, como lo hace Django. En cierto modo, Flask-RestPlus intenta ayudar a organizar un proyecto de Flask en crecimiento, pero sin que pierda su mínima sobrecarga, que es el mayor encanto de Flask.

El objetivo de esta serie es comenzar con una aplicación Flask simple e intentar abordar los siguientes puntos con un poco de Flask-RestPlus a la vez:

  1. Estructurar y documentar automáticamente una API (Parte 1)
  2. Garantizar la validación de la carga útil de solicitud / respuesta (Parte 2)

Aplicación de demostración

Comencemos con una API simple basada en Flask para una aplicación de gestión de conferencias:

desde el matraz de importación Frasco

aplicación = Frasco (__ nombre__)


@ app.route ("/ conferencias /")
def get_all__conferences ():
    "" "
    devuelve una lista de conferencias
    "" "


@ app.route ("/ conferencias /", métodos = ['POST'])
def add_conference ():
    "" "
    Agrega una nueva conferencia a la lista
    "" "


@ app.route ("/ conferencias / ")
def get_conference (id):
    "" "
    Muestra los detalles de una conferencia.
    "" "
@ app.route ("/ conferencias / ")
def edit_conference (id):
    "" "
    Edita una conferencia seleccionada
    "" "

Instalar Flask-RestPlus es fácil:

pip install Flask-RestPlus

Simplemente presentemos un objeto Api por ahora, intente envolver nuestra instancia de aplicación con él, reemplace los decoradores de enrutamiento y vea qué sucede:

desde el matraz de importación Frasco
de flask_restplus import Api
aplicación = Frasco (__ nombre__)
api = Api (aplicación = aplicación)
@ api.route ("/ conferencias /")
def get_all__conferences ():
    "" "
    devuelve una lista de conferencias
    "" "
@ api.route ("/ conferencias /", métodos = ['POST'])
def add_conference ():
    "" "
    Agrega una nueva conferencia a la lista
    "" "
@ api.route ("/ conferencias / ")
def get_conference (id):
    "" "
    Muestra los detalles de una conferencia.
    "" "
@ api.route ("/ conferencias / ")
def edit_conference (id):
    "" "
    Edita una conferencia seleccionada
    "" "

Tan pronto como se inicie la aplicación, obtendremos el siguiente error:

AttributeError: el objeto 'function' no tiene atributo 'as_view'

Esto se debe a que si desea utilizar RestPlus para algunas de sus funciones de Flask, debe incluirlas en una clase de alcance. No solo eso, sino que dentro de la clase adjunta, debe nombrar sus métodos, correspondientes a los métodos HTTP en los que se basa REST: GET, POST, PUT y DELETE:

@ api.route ("/ conferencias /")
clase ConferenceList (Recurso):
    def get (self):
        "" "
        devuelve una lista de conferencias
        "" "

Antes de que alguien comience a objetar, permítanme explicar por qué esto es útil. Flask-RestPlus utiliza el concepto Flask de "Vistas enchufables" para presentar un recurso (como en el recurso REST).

Seamos honestos. Si bien la mayoría de las aplicaciones de Flask comienzan de manera simple, muchas de ellas superan la idea inicial, y la acumulación de varias funciones de controlador en el alcance del módulo principal se convierte rápidamente en un desastre. Esta es la razón por la que existen los Flask Blueprints para ayudar a dividir la funcionalidad común en múltiples módulos.

Flask-RestPlus también hace un gran uso de Blueprints, como demostraré más adelante, pero los Recursos van un nivel de granularidad más allá. Una clase de recursos puede tener varios métodos, pero cada uno debe tener el nombre de uno de los verbos HTTP aceptados. ¿Qué sucede si necesita más de un método GET o POST para su API? Bueno, cree múltiples clases de recursos y coloque cada método en la clase de recurso correspondiente. Puede parecer un poco abrumador al principio, debido a la naturaleza de Flask, pero con un poco de juego, no será una obviedad en absoluto, y valdrá la pena tremendamente a la larga.

Veamos cómo se verá nuestra pequeña aplicación después de las transformaciones:

desde el matraz de importación Frasco
de flask_restplus import Api, Resource
aplicación = Frasco (__ nombre__)
api = Api (aplicación = aplicación)
@ api.route ("/ conferencias /")
clase ConferenceList (Recurso):
    def get (self):
        "" "
        devuelve una lista de conferencias
        "" "
    publicación de def (auto):
        "" "
        Agrega una nueva conferencia a la lista
        "" "
@ api.route ("/ conferencias / ")
Conferencia de clase (Recurso):
    def get (self, id):
        "" "
        Muestra los detalles de una conferencia.
        "" "
    def put (self, id):
        "" "
        Edita una conferencia seleccionada
        "" "

Con este pequeño gasto adicional (si considera que es un gasto general), obtiene tanto a cambio. Inicie la aplicación y apunte a http: // localhost: 5000. Verá que la página de índice se ha convertido en una interfaz de usuario Swagger, que muestra los puntos finales de API ya definidos, organizados en categorías (espacios de nombres):

Esto es ideal para documentar, jugar y compartir su esquema de API. Sin embargo, esto no es lo único que Flask-RestPlus hace por usted. Va más allá de simplemente documentar la API, para garantizar que la API cumpla con el esquema. En pocas palabras, Flask-RestPlus se asegura de que si ciertos parámetros de solicitud se marcan como obligatorios, o si se supone que los modelos de solicitud / respuesta tienen una determinada estructura, se verifican y validan en tiempo de ejecución. En mi opinión, esta es una verdadera ventaja de Flask-RestPlus, que se encuentra en la parte superior de una aplicación Flask. El ejemplo actual es demasiado simple para demostrar el verdadero poder de la clasificación y validación de solicitud / respuesta, pero ambos se describirán detalladamente en la Parte 2.

Espacios de nombres

Los espacios de nombres son opcionales y agregan un toque de organización adicional a la API, principalmente, desde el punto de vista de la documentación. Un espacio de nombres le permite agrupar recursos relacionados bajo una raíz común, y es fácil de crear:

ns_conf = api.namespace ('conferencias', descripción = 'Operaciones de conferencia')

Para poner ciertos recursos en un espacio de nombres dado, todo lo que necesita hacer es reemplazar @api con @ns_conf. Observe también que el nombre del espacio de nombres reemplaza el nombre del recurso, por lo que los puntos finales pueden simplemente referirse a /, en lugar de copiar el nombre del recurso una y otra vez:

desde el matraz de importación Frasco
de flask_restplus import Api, Resource
aplicación = Frasco (__ nombre__)
api = Api (aplicación = aplicación)
ns_conf = api.namespace ('conferencias', descripción = 'Operaciones de conferencia')
@ ns_conf.route ("/")
clase ConferenceList (Recurso):
    def get (self):
        "" "
        devuelve una lista de conferencias
        "" "
    publicación de def (auto):
        "" "
        Agrega una nueva conferencia a la lista
        "" "
@ ns_conf.route ("/ ")
Conferencia de clase (Recurso):
    def get (self, id):
        "" "
        Muestra los detalles de una conferencia.
        "" "
    def put (self, id):
        "" "
        Edita una conferencia seleccionada
        "" "

Después se notará que la pantalla de la interfaz de usuario de Swagger también ha cambiado, para reflejar el espacio de nombres:

Planos

Los Blueprints son una forma popular de diseñar aplicaciones modulares. Lo mismo se aplica a Flask-RestPlus. La versión de producción de nuestra aplicación seguramente superará los cuatro puntos finales con los que comenzamos. Puede haber otros recursos, o al menos, es posible que desee moverse para alejar su API de la raíz de su aplicación. Ambos casos son un candidato perfecto para un Blueprint. Muevamos todos nuestros puntos finales de API bajo / api / v1, sin tocar las rutas de ni siquiera uno de ellos. Este ejemplo proviene directamente de la documentación de Flask-RestPlus, y es lo suficientemente ilustrativo como para ayudar a cerrar este capítulo del viaje:

Cree un Blueprint de la forma habitual y, en lugar de ajustar nuestra instancia de aplicación con la API RestPlus, ajustaremos el Blueprint. De esta manera, independientemente de nuestra aplicación, somos libres de mover nuestra parte de la API a un módulo diferente: (por ejemplo, blueprint / api.py)

de Blueprint import Blueprint
de flask_restplus import Api
blueprint = Blueprint ('api', __name__)
api = Api (plano)
# Trae el resto de nuestro código API aquí

Esto deja solo un poquito de código de puente para introducir el Blueprint en la aplicación principal y establecer el prefijo de URL. La próxima vez que inicie su aplicación, solo se podrá acceder a los puntos finales de la API con el prefijo de URL especificado (/ api / v1).

desde el matraz de importación Frasco
desde apis import blueprint como api
aplicación = Frasco (__ nombre__)
app.register_blueprint (api, url_prefix = '/ api / 1')

Por último, pero no menos importante, siempre es una buena idea alejar la documentación de Swagger UI de la raíz. Como en todo lo demás en RestPlus, esta parte también es extremadamente fácil. Puede anular la ubicación predeterminada pasando un parámetro adicional al inicializador:

api = Api (aplicación = aplicación, doc = '/ docs')

Esto resume la primera parte de mi serie. Espero que haya sido informativo y que lo ayude a estructurar mejor sus API REST basadas en Flask en el futuro. Hasta la próxima!

Otras lecturas