Crear una Api Restful en Flask con autenticación requerida

Video thumbnail

Crear una Restful API en Flask es una de las formas más simples y efectivas de conectar aplicaciones y sistemas. En mi experiencia, Flask ofrece la flexibilidad ideal para construir servicios web limpios, seguros y fáciles de mantener. A lo largo de esta guía te muestro cómo lo hago paso a paso, desde la instalación hasta la autenticación, incluyendo código real y consejos prácticos que me han funcionado en proyectos reales.

Nos quedamos en como podemos usar Flask-SocketIO para crear aplicaciones en tiempo real con Python.

Qué es una API RESTful y por qué usar Flask para crearla

Una API RESTful sigue el principio de que cada recurso del sistema se representa mediante una URL única y se manipula a través de los métodos HTTP estándar:

  • GET: obtener información.
  • POST: crear nuevos recursos.
  • PUT/PATCH: actualizar datos existentes.
  • DELETE: eliminar registros.

Una Api Rest es una estructura en base a funciones que son muy empleadas hoy en día para comunicar sistemas o distintas aplicaciones, cada recurso que podemos crear son simplemente funciones que a la final son traducidas mediante URLs que son consumidas vía HTTP y los métodos del mismo:

Por lo tanto, dependiendo del método HTTP nosotros podemos dar una respuesta distinta, siempre respetando el propósito de cada tipo de petición HTTP.

Flask es perfecto para esto porque permite construir una API ligera sin configuración excesiva. Con solo unas líneas de código ya tienes endpoints funcionando, y con el paquete Flask-Restful puedes estructurar todo en clases bien organizadas.

Diferencias entre REST y RESTful

“REST” es el estilo arquitectónico, mientras que “RESTful” se refiere a la implementación práctica que cumple con esos principios: recursos bien definidos, comunicación vía HTTP y formato JSON.

Ventajas de usar Flask frente a otros frameworks

  • Ligereza: ideal para microservicios o APIs específicas.
  • Extensibilidad: compatible con extensiones como Flask-Restful, Flask-HTTPAuth, SQLAlchemy.
  • Curva de aprendizaje corta: se domina rápidamente si ya conoces Python.

Preparar el entorno de trabajo: Herramientas necesarias

Así que, para trabajar con este paquete, lo primero que necesitamos es nuestra aplicación en Flask y luego instalar el paquete para el Restful mediante:

$ pip install flask-restful

Estructura base del proyecto:

my_app/
│
├── app.py
├── models.py
├── rest/
│   ├── controllers.py
│   └── __init__.py
└── auth/
   ├── model/
   │   └── user.py

Ahora ya estamos listos para poder emplear este paquete. 

Mejores prácticas y conociendo flask restful

La clave para una buena API no es solo que funcione, sino que sea predecible y coherente.
Algunos errores comunes que conviene evitar:

  • No validar la entrada del usuario antes de operar con la base de datos.
  • No devolver mensajes claros ante errores 400 o 404.
  • No documentar los endpoints ni sus parámetros.

Siempre recomiendo probar los endpoints con Postman o CURL y documentar usando Swagger o OpenAPI si la API crecerá.

Conversión automática de objeto a diccionario

Otro elemento muy interesante es que nosotros tenemos un decorador llamado marshal_with que nos permite indicar de una que campos queremos devolver del modelo con el cual estamos trabajando cada vez que retornamos el objeto; para eso primero tenemos que definir la estructura que sostenga lo anteriormente comentado:

resource_fields = {
   'id': fields.Integer,
   'name' : fields.String
}

Este decorador también recibe de manera opcional el envoltorio del json que va a devolver que podemos configurar mediante el envelope.

Implementando el CRUD completo

Cómo funcionan los métodos HTTP

Cada método HTTP tiene un propósito concreto y conviene respetarlo siempre. En mis proyectos, me ha ayudado mucho mantener esta convención porque simplifica el mantenimiento del código y la documentación.

Creación de las clases Resource

Con Flask-Restful, cada clase Resource representa un conjunto de operaciones sobre un recurso.
Me gusta separar las clases que no necesitan un id (para listar o crear) de las que sí lo requieren (para leer, actualizar o borrar).

Vamos a crear un par de clases por cada entidad que queramos hacer gestionables es decir realizar el CRUD sobre algún modelo que recuerda que un modelo es nuestra conexión con una tabla en la base de datos.

Antes de comenzar

Recuerda que aquí no vamos a tratar como trabajar con SQLAlchemy para realizar las conexiones sobre nuestra base de datos con nuestro proyecto en Flask, simplemente vamos a realizar las consultas tipo CRUD para operar la base de datos.

Seguramente te estás preguntando porqué necesitamos dos clases y no solo una, la respuesta se debe a la definición de en las rutas, por lo tanto vamos a tener:

  1. Una clase para los métodos que NO necesiten de una pk o id para poder operar
  2. Una clase para los métodos que SÍ necesiten de una pk o id para poder operar

Por lo tanto las rutas y configuración que hacemos quedan así:

from flask_restful import Api
from proyect_app.rest.controllers import BookApi, BookApiId
 
api = Api(app) #, decorators=[csrf.exempt]
api.add_resource(BookApi, '/api/book')
api.add_resource(BookApiId, '/api/book/<string:id>')

Así que vamos a crear una para trabajar sin los IDs o PKs; como puedes suponer, esto serían para los métodos de Post, para crear un registro y para get, para obtener TODOS los registros.

Vamos a trabajar con el siguiente modelo:

class Category(db.Model):
   __tablename__ = 'categories'
   id = db.Column(db.Integer, primary_key=True)
   name = db.Column(db.String(255))

Funcionalidades adicionales sobre nuestro paquete

Nuestro paquete para lograr una Api Restful, trae varios elementos interesantes que nos facilitan bastante la vida para crear este tipo de APIs tipo CRUD.

Método POST: crear nuevos recursos

@marshal_with(resource_fields, envelope="categoria")
def post(self):
    parser = reqparse.RequestParser()
    parser.add_argument('name', required=True, help="No mandaste el nombre")
    args = parser.parse_args()
    c = Category(args['name'])
    db.session.add(c)
    db.session.commit()

Aquí vamos a trabajar para crear un registro, así que vamos a crear otra clase que SI van a trabajar con los IDs:

Método GET: obtener todos los registros

@marshal_with(resource_fields, envelope="categorias")
def get(self):
    return Category.query.all()

Aquí vamos a devolver un registro por su identificador:

Métodos PATCH/PUT: actualizar datos

@marshal_with(resource_fields, envelope="categoria")
def patch(self, id):
    parser = reqparse.RequestParser()
    parser.add_argument('name', required=True)
    args = parser.parse_args()
    c = Category.query.get_or_404(id)
    c.name = args['name']
    db.session.commit()
    return c

Método DELETE: eliminar un registro

def delete(self, id):
    c = Category.query.get_or_404(id)
    db.session.delete(c)
    db.session.commit()
    return {'msj': 'ok'}

Finalmente, todo el código de ambas clases:

 
from flask import request
from flask_restful import Resource, abort, reqparse, fields, marshal_with
from flask_httpauth import HTTPBasicAuth
 
from my_app.product.models import Category
from my_app.auth.model.user import User
from my_app import user_manager
from my_app import db
 
resource_fields = {
   'id': fields.Integer,
   'name' : fields.String
}
 
auth = HTTPBasicAuth()
 
@auth.verify_password
def verify_password(username, password):
   user = User.query.filter_by(username=username).first()
 
   if not user or not user_manager.verify_password(password_hash=user.password, password=password):
       return False
   return True
 
class Base:
   def category_to_json(self,category):
       return {
           'id': category.id,
           'name': category.name,
       }
 
   def abort_if_doesnt_exist(self, id, json=True):
       category = Category.query.get(id)
       if category is None:
           abort(404, message="Categoría {} no existe".format(id))
       if json:
           return self.category_to_json(category)
 
       return category
 
class CategoryRestFul(Resource,Base):
 
   @auth.login_required
   @marshal_with(resource_fields, envelope="categoria")
   def get(self, id):
       return self.abort_if_doesnt_exist(id,False)
 
   @marshal_with(resource_fields, envelope="categoria")
   def patch(self, id):
 
       parser = reqparse.RequestParser()
       parser.add_argument('name', required=True, help="No mandastes el nombre")       args = parser.parse_args()
 
       c = self.abort_if_doesnt_exist(id, False)
 
       c.name = args['name'] #request.form
 
       db.session.add(c)
       db.session.commit()
 
       return self.abort_if_doesnt_exist(c.id,False)
 
   def delete(self, id):
 
       c = self.abort_if_doesnt_exist(id, False)
 
       db.session.delete(c)
       db.session.commit()
 
       return {'msj':'ok'}
 
 
class CategoryRestFulList(Resource,Base):
 
   @marshal_with(resource_fields, envelope="categorias")
   def get(self):
       return Category.query.all()
 
   @marshal_with(resource_fields, envelope="categoria")
   def post(self):
      
       parser = reqparse.RequestParser()
       parser.add_argument('name',required=True, help="No mandastes el nombre")
       args = parser.parse_args()
 
       c = Category(args['name'])
       db.session.add(c)
       db.session.commit()
 
       return c
 
class CategoryRestFulListSearch(Resource,Base):
 
   @marshal_with(resource_fields, envelope="categoria")
   def get(self):
 
       parser = reqparse.RequestParser()
       parser.add_argument('search',required=True, help="Tienes que especificar la busqueda")
       args = parser.parse_args()
 
       return Category.query.filter(Category.name.like('%{0}%'.format(args['search']))).all()

Otras funcionalidades 

Vamos a crear una clase auxiliar que nos permitirá agregar algunas funcionalidades adicionales sobre las clases tipo rest; por ejemplo, la de protección con login.

Y aquí definir nuestros métodos

@marshal_with(resource_fields, envelope="categorias")
def get(self):
  return Category.query.all()

Con esta función vamos a obtener todos los registros de nuestra base de datos:

Puntos a tener en cuenta

Como puedes ver, las operaciones que hacemos en la Rest Api no tienen nada de raro, algunas validaciones, operaciones sobre la base de datos y poco más, pero como puedes ver, tenemos un mecanismo bastante práctico para manipular u obtener los datos de nuestro usuario; los datos que nos pasa vía un formulario, una petición axio, o cualquier Api que emplees para mandar datos vía HTTP, los obtenemos mediante la clase de RequestParser; lo ideal de esto, es que nosotros podemos definir validaciones en la eficiente de la función add_argument y por lo tanto podemos estar 100 por ciento seguro que los datos que estamos trabajando son válidos.

Seguridad y Autenticación requerida en la Restful con Flask

Como puedes ver, estamos empleando una función que a la final funciona como decorador sobre nuestros recursos Rest para hacer la autenticación requerida sobre nuestra Rest Api; esto es un paquete de un tercero que puedes adaptar a otro tipo de servicios pero que funciona bastante bien con Flask Restful; simplemente tenemos que definir una función base en la cual realizamos las operaciones sobre nuestro modelo de usuarios, sea lo que sea que quieras verificar en la fase de autenticación requerida de la Rest Api lo puedes definir en esa función.

En este ejemplo tenemos un sencillo ejemplo en el cual buscamos primero por el username y su existe luego verificamos la contraseña que están en formato hash en la base de datos, por lo tanto, es una función que se encarga de hacer un login manual, y que empleamos perfectamente en nuestra Rest Api como autenticación requerida.

@auth.verify_password
def verify_password(username, password):
    user = User.query.filter_by(username=username).first()
    if not user or not user_manager.verify_password(password_hash=user.password, password=password):
        return False
    return True

Recuerda que para emplear este paquete de autenticación tienes que instalarlo con:

pip install Flask-HTTPAuth

Conclusión

En mi experiencia desarrollando APIs RESTful con Flask, aprendí que usar flask-restful reduce mucho el esfuerzo de mantener el código limpio y modular. Separar las clases según usen o no id, validar la entrada con reqparse y proteger las rutas con HTTPAuth son prácticas que marcan la diferencia entre un prototipo y una API lista para producción.

Preguntas frecuentes (FAQ)

¿Qué diferencia hay entre Flask y Flask-Restful?
Flask es el microframework base; Flask-Restful agrega una capa que facilita la creación de APIs estructuradas con recursos y métodos HTTP.

¿Cómo puedo proteger mi API con autenticación por tokens?
Puedes integrar Flask-JWT o Flask-JWT-Extended para manejar tokens JWT en lugar de autenticación básica.

¿Flask es recomendable para proyectos grandes?
Sí, pero requiere modularización: dividir la app en blueprints y usar una arquitectura clara de paquetes.

¿Cómo probar mi API con Postman o cURL?
Con Postman puedes enviar peticiones a cada endpoint; con CURL, usa comandos como curl -X GET http://localhost:5000/api/book.

Rest Api en Flask con JSON Web Token en MySQL o MongoDB

Rest Api en Flask con JSON Web Token en MySQL o MongoDB
Video thumbnail

Proteger una REST API con Json Web Tokens (JWT) en Flask es una de las formas más prácticas y seguras de manejar autenticación sin depender de sesiones. En esta guía te muestro cómo implementar JWT en Flask desde cero, integrando además FlaskUser y MongoDB, tal como lo he hecho en mis propios proyectos.

Json Web Token (JWT) es solo un string que enviamos del navegador o cliente a nuestro servidor en el header de la petición o request; y esto con el propósito de validar y verificar al cliente; por supuesto, el servidor es el que se encarga de generar el JWT con alguna condición sobre un usuario existente que generalmente es comprobar una pareja de usuario y contraseña.

Qué es un Json Web Token (JWT) y cómo funciona

Un JWT es un string codificado que sirve para verificar la identidad de un cliente frente al servidor. En lugar de mantener sesiones, el cliente envía el token en el encabezado de cada petición HTTP.

Estructura de un JWT: Header, Payload y Signature

El token se compone de tres partes separadas por puntos:

header.payload.signature
  • Header: indica el tipo de token y algoritmo de cifrado (por ejemplo, HS256).
  • Payload: contiene los datos del usuario o claims.
  • Signature: garantiza que el token no haya sido alterado.

Cómo se usa un token JWT en una REST API

El flujo es simple:

  • El cliente inicia sesión y envía usuario + contraseña.
  • El servidor valida las credenciales y genera el token.
  • El cliente incluye ese token en el header de cada request:
    • Authorization: Bearer <token>
  • Flask verifica la validez del token antes de permitir acceso a los recursos.

Ventajas frente a sesiones tradicionales

Escalable y sin necesidad de almacenar sesiones.

Compatible con microservicios y frontends SPA.

Fácil de revocar o expirar.

Requisitos

Vamos a desarrollar en varias tecnologías como puedes ver, pero para hacer un pequeño resumen, vas a necesitar:

En esta entrada vamos a conocer cómo podemos proteger una Rest Api creada con el paquete de FlaskRestFul con Json Web Token, el famoso JWT empleando el paquete de FlaskJWT empleando por supuesto Flask; además de esto, vamos a emplear el famoso paquete llamado FlaskUser para manejar las credenciales de acceso del usuario; es decir, usuario y contraseña.

Instalación de dependencias

$ pip install flask flask-restful flask-jwt flask-user mongoengine

Creando los métodos necesarios para trabajar con Flask JWT

En esta sección vamos a ver qué es lo que tenemos que configurar para poder emplear Flask JWT en nuestro proyecto en Flask; por supuesto, estas son configuraciones y desarrollos generales o de ejemplos que lo puedes emplear en cualquier esquema; en nuestro caso, vamos a emplear la el paquete de FlaskUser para manejar todo lo que tenga que ver con el login; en este caso nos interesa la función para manejar las credenciales específicamente el uso del password o contraseña.

Implementando autenticación JWT en Flask

Dentro de tu proyecto Flask, configura la base de datos y el manejador de usuarios:

from flask_user import UserManager
from flask_mongoengine import MongoEngine
app = Flask(__name__)
app.config['MONGODB_SETTINGS'] = {'db': 'demo_jwt'}
db = MongoEngine(app)
user_manager = UserManager(app, db, User)

Uso MongoDB, pero puedes adaptar fácilmente a SQLAlchemy si trabajas con MySQL

Creamos el modelo para el usuario:

import mongoengine as models
from flask_user import UserMixin
import mongoengine as models
from flask_user import UserMixin
  
class User(models.Document, UserMixin):
   active = models.BooleanField(default=True)
 
   # User authentication information
   username = models.StringField(default='')
   password = models.StringField()
 
   # User information
   first_name = models.StringField(default='')
   last_name = models.StringField(default='')
 
   def __str__(self):
       return self.username
 
class UserBase(object):
   def __init__(self, id, username, password):
       self.id = str(id)
       self.username = username
       self.password = password
  
   def __str__(self):
     return f"User id: {self.id}"

La clase de UserBase solamente la tienes que crear si vas a emplear MongoDB para hacer el parseo a json que es empleado internamente por FlaskJWT; simplemente empleamos esta clase como intermediario para devolver el usuario autenticado.

Las funciones de identidad y login o autenticación el Flask JWT

Para poder emplear el paquete de FlaskJWT, tenemos que crear en su configuración mínima un par de funciones, la primera para obtener el usuario autenticado dado el identificador, llamada identity y otra función para manejar el login, es decir, comparar el usuario y el password; así que:

from .models import User, UserBase
from proyect_app import user_manager
 
def autheticate(username, password):
   user = User.objects(username=username).first()
  
   if user_manager.verify_password(password,user):
       userBase = UserBase(user.id, user.username, user.password)
       return userBase
 
def identity(payload):
   user_id = payload['identity']
   user = User.objects(id=user_id).first()
   return user

Función authenticate

Para esta función que recibe por defecto dos parámetros, el usuario y la contraseña, buscamos primero por el usuario y luego verificamos la contraseña, que al emplear FlaskUser la tenemos fácil y podemos comprobar la contraseña mediante la función que nos provee FlaskUser llamada verify_password; a la final, si todo esta ok, retornamos el usuario autenticado.

Función identity

Esta función recibe el payload y nos interesa consumir el ID del usuario que lo tenemos como payload['identity'] y retornar el usuario; así de simple.

Configuraciones globales:

A nivel de nuestro proyecto, tenemos que crear las configuraciones para JWT; viene siendo lo usual y una vez creada las dos funciones anteriores, las importamos y la pasamos como parámetros a la clase correspondiente para Flask JWT:

# jwt
from proyect_app.user.jwt import autheticate, identity
jwt = JWT(app, autheticate, identity)

Las importaciones:

from flask_user import UserManager
from flask_jwt import JWT

Y configuramos nuestro UserManager de nuestro FlaskUser:

user_manager = UserManager(app, db, User)

Configurar la protección en MySQL o MongoDB

Ahora, según el motor de base de datos que estamos empleando, por ejemplo MongoDB con el conector que estamos empleando en nuestro curso de MongoDB con Flask y Django:

db = MongoEngine(app)

o con SQLAlchemy que podemos emplear por ejemplo, MySQL:

db=SQLAlchemy(app)

Proteger la Rest Api ful en Flask

Finalmente, protegemos la RestApi en FlaskRestFul; que para modo de ejemplo, tenemos una clase de tipo recurso y le indicamos el decorador correspondiente:

class BookApi(Resource):
  decorators = [jwt_required()]

Puedes encontrar el proyecto completo del curso de Flask con MongoDB en:

Para el modelo de usuario, que recuerda que también estamos empleando FlaskUser, lo usual, al menos el usuario y la contraseña: 

Dependencias necesarias o instalaciones

  1. https://pythonhosted.org/Flask-JWT/
  2. https://flask-user.readthedocs.io/en/latest/
  3. https://flask-restful.readthedocs.io/en/latest/
  4. http://docs.mongoengine.org/projects/flask-mongoengine/en/latest/

Probando la API con Postman

Una vez configurado el JWT, podemos probarlo.

Obteniendo el token de autenticación

Envía una petición POST a /auth con:

{
 "username": "usuario",
 "password": "clave"
}

Usando el token en peticiones protegidas

Copia el token generado y colócalo en los headers:

Authorization: Bearer <token>

Posibles respuestas

  • Código    Descripción
  • 200    Petición válida, acceso permitido
  • 401    Token inválido o ausente
  • 403    Token expirado o usuario no autorizado

Postman fue la herramienta más práctica para verificar el flujo completo, especialmente al simular errores de autenticación.

Solución a errores comunes con FlaskJWT y MongoDB

Durante la implementación surgen algunos contratiempos.

Error “ObjectId is not JSON serializable”

Este problema aparece al usar MongoDB:

FlaskJWT intenta convertir el ID del usuario directamente en JSON, lo que genera un fallo.

Solución: devolver una instancia de UserBase en lugar del modelo MongoDB original.
Esto garantiza que el ID se maneje como string.

Referencias circulares al importar funciones

Evita importar authenticate e identity en la parte superior de tu __init__.py.
Hazlo después de inicializar app y db para prevenir errores de importación.

Estructura modular recomendada

Divide tu aplicación en carpetas:

project_app/
├── user/
│    ├── models.py
│    ├── jwt.py
├── api/
│    ├── resources/
├── app.py


En mi caso, organizar el código de esta forma simplificó mucho las pruebas y mantenimiento.

Buenas prácticas de seguridad y mantenimiento

Expiración y renovación de tokens

Configura la expiración:

app.config['JWT_EXPIRATION_DELTA'] = timedelta(seconds=3600)

Para renovaciones, implementa un endpoint de refresh si usas Flask-JWT-Extended.

Dónde almacenar los JWT

Evita guardarlos en localStorage del navegador.

Usa cookies seguras (HttpOnly) o almacenamiento en memoria.

Flask-JWT vs Flask-JWT-Extended
Aspecto    Flask-JWT    Flask-JWT-Extended
Simplicidad    ✅ Fácil de configurar    ⚙️ Más opciones avanzadas
Renovación de tokens    ❌ No    ✅ Sí
Soporte activo    Limitado    Amplio
Recomendado para    APIs simples    Proyectos en producción

Conclusión

Implementar JWT en Flask permite una autenticación más moderna y escalable.
Combinar FlaskUser con FlaskJWT me permitió aprovechar lo mejor de ambos mundos: la verificación robusta de contraseñas y la flexibilidad de los tokens.

Si planeas trabajar con MongoDB, recuerda usar una clase base personalizada para evitar errores de serialización y mantener la arquitectura modular desde el inicio.

❓ Preguntas frecuentes (FAQ)

  • ¿Cuál es la mejor librería para JWT en Flask?
    • Flask-JWT-Extended es la más completa actualmente, aunque Flask-JWT sigue siendo útil para proyectos pequeños.
  • ¿Puedo usar JWT sin base de datos en Flask?
    • Sí, siempre que el payload contenga información suficiente para validar al usuario.
  • ¿Cómo revocar un token en Flask?
    • Guarda los tokens en una lista de revocación (blacklist) y comprueba cada petición.
  • ¿Qué diferencia hay entre autenticación y autorización?
    • La autenticación verifica quién eres; la autorización determina qué puedes hacer dentro del sistema.

Siguiente paso, aprende a configurar los CORS para que puedas conectar una aplicación externa a la Rest Api en Flask.

Acepto recibir anuncios de interes sobre este Blog.

Vamos a aprender a crear una Api Restful en Flask fácilmente empleando el paquete llamado flask_restful que como su nombre indica nos permite crear una Api Rest de tipo CRUD fácilmente.

| 👤 Andrés Cruz

🇺🇸 In english