14. Servicios Web con FastAPI#

En este capítulo final presentamos de manera introductoria el concepto de servicios web, utilizando un ejemplo sencillo implementado con el framework FastAPI. El objetivo no es profundizar en el desarrollo de APIs ni en los detalles del protocolo HTTP, sino mostrar cómo un modelo computacional puede exponerse como un servicio accesible a través de la red.

Entre otras ventajas, este enfoque nos permite compartir funcionalidad desarrollada en un lenguaje o tecnología particular con otros componentes escritos en lenguajes distintos, o incluso con aplicaciones desarrolladas por terceros. Asimismo, facilita la escalabilidad de los algoritmos, al permitir su ejecución de manera distribuida mediante servicios independientes que se comunican a través de protocolos estándar.

Este enfoque es especialmente relevante en el contexto de este libro, ya que permite integrar técnicas como control difuso, metaheurísticas o aprendizaje automático dentro de arquitecturas modernas de software, por ejemplo, micro-servicios o funciones serverless.

14.1. ¿Qué es un servicio web?#

Un servicio web es una aplicación que expone funcionalidad a través de una interfaz accesible mediante la red, típicamente utilizando el protocolo HTTP. Estos servicios pueden intercambiar información también utilizando formatos web com JSON o XML. Aunque inicialmente los servicios web se basaban en protocolos complejos como SOAP o arquitecturas orientadas a brokers, en la actualidad suelen implementarse utilizando mecanismos más simples y familiares para los desarrolladores web.

En este capítulo utilizaremos el patrón REST (Representational State Transfer), en el cual:

  • Cada operación se asocia a una URL (endpoint).

  • Los parámetros se envían como parte de la ruta o de la consulta.

  • Las respuestas se devuelven típicamente en formato JSON.

  • Las operaciones se invocan utilizando métodos HTTP como GET o POST, donde el verbo HTTP indica el tipo de acción que se realiza (consulta, creación, actualización, etc.).

14.2. FastAPI#

Para ilustrar estos conceptos utilizaremos la librería FastAPI, un framework moderno para Python que permite construir servicios web de forma rápida, clara y eficiente. FastAPI se apoya en anotaciones de tipos para definir los parámetros de entrada y generar automáticamente documentación interactiva, lo que lo hace especialmente adecuado para exponer modelos computacionales y prototipos académicos como servicios accesibles desde la red.

En este ejemplo vamos a implementar un controlador difuso encapsulado dentro de un servicio REST. De esta forma, la lógica de inferencia se mantiene separada de la interfaz de acceso, y puede ser utilizada por aplicaciones externas, independientemente del lenguaje o plataforma en la que estén implementadas.

14.3. Un servicio difuso para el cálculo de propinas#

Como ejemplo, implementamos un servicio web que calcula una propina utilizando un sistema de control difuso, similar a los ejemplos clásicos presentados en capítulos anteriores.

El servicio recibe dos valores numéricos (float):

  • comida_val: calidad de la comida (0–10),

  • servicio_val: calidad del servicio (0–10),

y devuelve un valor numérico correspondiente a la propina recomendada.

Implementación del servicio#

El código completo del servicio es el siguiente:

 1from fastapi import FastAPI
 2import numpy as np
 3import skfuzzy as fuzz
 4from skfuzzy import control as ctrl
 5
 6app = FastAPI()
 7
 8@app.get("/")
 9def read_root():
10    return {"Hola": "Mundo"}
11
12@app.get("/propina/{comida_val}/{servicio_val}")
13def calc_propina(comida_val: float, servicio_val: float):
14    # Variables difusas de entrada y salida
15    comida = ctrl.Antecedent(np.arange(0, 11, 1), 'comida')
16    servicio = ctrl.Antecedent(np.arange(0, 11, 1), 'servicio')
17    propina = ctrl.Consequent(np.arange(0, 26, 1), 'propina')
18
19    # Funciones de membresía automáticas
20    comida.automf(3)
21    servicio.automf(3)
22
23    # Dominio de las funciones
24    propina['low'] = fuzz.trimf(propina.universe, [0, 0, 13])
25    propina['medium'] = fuzz.trimf(propina.universe, [0, 13, 25])
26    propina['high'] = fuzz.trimf(propina.universe, [13, 25, 25])
27
28    # Reglas difusas
29    rule1 = ctrl.Rule(comida['poor'] | servicio['poor'], propina['low'])
30    rule2 = ctrl.Rule(servicio['average'], propina['medium'])
31    rule3 = ctrl.Rule(servicio['good'] | comida['good'], propina['high'])
32
33    # Controlador difuso
34    propina_ctrl = ctrl.ControlSystem([rule1, rule2, rule3])
35    calcula_propina = ctrl.ControlSystemSimulation(propina_ctrl)
36
37    # Entradas
38    calcula_propina.input['comida'] = comida_val
39    calcula_propina.input['servicio'] = servicio_val
40    calcula_propina.compute()
41
42    # Salida
43    return {"propina": calcula_propina.output['propina']}

Estructura del servicio#

El servicio define dos endpoints principales:

  • / Devuelve un mensaje simple, útil para verificar que el servicio está activo.

  • /propina/{comida_val}/{servicio_val} Ejecuta el sistema difuso y devuelve la propina calculada.

Cada vez que se llama al endpoint de propina:

  1. Se construye el sistema difuso.

  2. Se asignan los valores de entrada.

  3. Se ejecuta la inferencia.

  4. Se devuelve el resultado como un objeto JSON.

Desde el punto de vista del cliente, el sistema difuso se comporta como una caja negra, accesible mediante una llamada HTTP.

Ejecución del servicio#

El servicio puede ejecutarse con:

uvicorn main:app --reload

Una vez en ejecución, es posible acceder a:

  • http://localhost:8000/

  • http://localhost:8000/propina/7.2/8.3

FastAPI genera automáticamente una interfaz de documentación interactiva en:

http://localhost:8000/docs

14.4. Consumo del servicio desde Python#

Una de las principales ventajas de exponer funcionalidad mediante un servicio web es que puede ser utilizada desde múltiples interfaces, no únicamente desde un navegador web. Cualquier cliente capaz de realizar solicitudes HTTP puede interactuar con el servicio, independientemente del lenguaje o plataforma en la que esté escrito.

En esta sección mostramos cómo consumir el servicio de cálculo de propina implementado con FastAPI desde un script de Python, utilizando la biblioteca requests.

La librería requests es el cliente HTTP de facto en Python y permite enviar peticiones de manera simple y legible.

Supongamos que el servicio se está ejecutando localmente en la dirección:

http://127.0.0.1:8000

y que queremos invocar el endpoint:

/propina/{comida_val}/{servicio_val}

donde comida_val y servicio_val son valores numéricos en el rango [0, 10].

El siguiente script realiza una petición HTTP GET al servicio y procesa la respuesta en formato JSON:

 1import requests
 2
 3# URL base del servicio
 4base_url = "http://127.0.0.1:8000"
 5
 6# Parámetros de entrada
 7comida_val = 8.0
 8servicio_val = 9.0
 9
10# Construcción de la URL del endpoint
11url = f"{base_url}/propina/{comida_val}/{servicio_val}"
12
13# Envío de la petición GET
14response = requests.get(url)
15
16# Verificamos que la respuesta sea correcta
17if response.status_code == 200:
18    data = response.json()
19    print("Propina calculada:", data["propina"])
20else:
21    print("Error en la solicitud:", response.status_code)

En este ejemplo:

  • Se construye la URL del endpoint incorporando los parámetros en la ruta.

  • Se envía una petición HTTP GET utilizando requests.get.

  • La respuesta se interpreta como un objeto JSON mediante response.json().

  • El valor de la propina se extrae directamente del diccionario resultante.

Es importante notar que este script no depende en absoluto de FastAPI ni de la implementación interna del controlador difuso. Desde el punto de vista del cliente:

  • El servicio es una caja negra.

  • La comunicación se realiza exclusivamente mediante HTTP.

  • Los datos se intercambian en un formato estándar (JSON).

Este mismo servicio podría ser consumido:

  • desde otro lenguaje de programación (por ejemplo JavaScript, Java o C++),

  • desde una aplicación móvil,

  • desde un sistema embebido,

  • o como parte de una arquitectura distribuida basada en microservicios.

Este ejemplo ilustra cómo los modelos desarrollados a lo largo del libro pueden integrarse fácilmente en sistemas más grandes, sin necesidad de reescribir la lógica principal ni acoplarla a una interfaz específica.

14.5. Resumén del capítulo#

En este capítulo se presentó un ejemplo ilustrativo de cómo podemos desacoplar nuestros algoritmos de un lenguaje de programación o arquitectura específica y exponerlos como un servicios independientes utilizando el protocolo HTTP.

Más que profundizar en los detalles de FastAPI, el objetivo de este capítulo es mostrar cómo las técnicas vistas a lo largo del libro pueden integrarse de forma natural en sistemas distribuidos y orientados a servicios.