Tutorial API REST Flask: Crear una API con Python y SQLAlchemy paso a paso
En este tutorial API REST Flask, aprenderás cómo construir una API RESTful completa utilizando Python, Flask y SQLAlchemy. Este recurso está pensado para desarrolladores principiantes e intermedios que desean iniciarse en el desarrollo backend con Flask desde cero, incluyendo la configuración del entorno, creación de modelos, definición de rutas para operaciones CRUD, validación de datos y pruebas básicas.
Introducción
El desarrollo backend Flask es especialmente popular gracias a la sencillez y flexibilidad del framework junto con SQLAlchemy, una potente herramienta de administración de bases de datos basada en ORM (Object-Relational Mapping). Al finalizar este tutorial, tendrás una API que puede crear, leer, actualizar y eliminar recursos almacenados en una base de datos SQLite.
1. Configuración del Entorno de Desarrollo
Antes de empezar, asegúrate de tener instalado Python 3.6 o superior. Vamos a crear un entorno virtual para mantener las dependencias aisladas.
Paso 1. Crear y activar entorno virtual
En tu terminal ejecuta:
1 2 3 4 5 6 7 8 9 |
# Crear entorno virtual python -m venv venv # En Linux/macOS activar source venv/bin/activate # En Windows activar venv\Scripts\activate |
Paso 2. Instalar dependencias
Instalamos Flask, SQLAlchemy y Flask-Marshmallow para manejo y validación de datos:
1 2 |
pip install Flask SQLAlchemy Flask-Marshmallow marshmallow-sqlalchemy |
Además, instalaremos pytest para las pruebas básicas:
1 2 |
pip install pytest |
2. Creación del Proyecto y Estructura Básica
Vamos a crear un archivo principal para nuestro proyecto llamado app.py
.
Tu estructura inicial debería ser:
1 2 3 4 5 6 |
/tu-proyecto │ ├── app.py ├── requirements.txt (opcional) └── venv/ |
Guarda las dependencias con:
1 2 |
pip freeze > requirements.txt |
3. Creación de Modelos SQLAlchemy
SQLAlchemy permite definir modelos que representan las tablas de la base de datos.
Paso 1. Configurar Flask y SQLAlchemy en app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow app = Flask(__name__) # Configuración de base de datos SQLite app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Inicializar ORM y Marshmallow db = SQLAlchemy(app) ma = Marshmallow(app) |
Paso 2. Crear un modelo ejemplo: User
1 2 3 4 5 6 7 8 |
class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) def __repr__(self): return f'<User {self.name}>' |
Paso 3. Crear esquema para serialización y validación
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from marshmallow import fields class UserSchema(ma.SQLAlchemyAutoSchema): class Meta: model = User load_instance = True name = fields.String(required=True) email = fields.Email(required=True) user_schema = UserSchema() users_schema = UserSchema(many=True) |
Paso 4. Crear base de datos
Ejecuta el siguiente comando para crear la base SQLite:
1 2 3 4 5 |
if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True) |
Luego inicia tu aplicación con:
1 2 |
python app.py |
La base database.db
se creará automáticamente.
4. Definición de Rutas RESTful para Operaciones CRUD
Vamos a crear las seis rutas principales para gestionar usuarios:
- Obtener todos los usuarios (GET /users)
- Obtener un usuario por id (GET /users/
) - Crear un nuevo usuario (POST /users)
- Actualizar un usuario existente (PUT /users/
) - Eliminar un usuario (DELETE /users/
)
Paso 1. Importar módulos necesarios
1 2 |
from flask import request, jsonify |
Paso 2. Definir ruta para obtener todos los usuarios
1 2 3 4 5 6 |
@app.route('/users', methods=['GET']) def get_users(): all_users = User.query.all() result = users_schema.dump(all_users) return jsonify(result), 200 |
Paso 3. Definir ruta para obtener usuario por ID
1 2 3 4 5 |
@app.route('/users/<int:id>', methods=['GET']) def get_user(id): user = User.query.get_or_404(id) return user_schema.jsonify(user), 200 |
Paso 4. Ruta para crear nuevo usuario
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@app.route('/users', methods=['POST']) def create_user(): json_data = request.get_json() if not json_data: return jsonify({'message': 'No input data provided'}), 400 # Validar datos try: user = user_schema.load(json_data) except Exception as e: return jsonify({'errors': str(e)}), 422 # Verificar email existente if User.query.filter_by(email=user.email).first(): return jsonify({'message': 'User with this email already exists'}), 400 # Agregar nuevo usuario db.session.add(user) db.session.commit() return user_schema.jsonify(user), 201 |
Paso 5. Ruta para actualizar usuario
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@app.route('/users/<int:id>', methods=['PUT']) def update_user(id): user = User.query.get_or_404(id) json_data = request.get_json() if not json_data: return jsonify({'message': 'No input data provided'}), 400 # Validar y cargar datos parcialmente try: data = user_schema.load(json_data, partial=True) except Exception as e: return jsonify({'errors': str(e)}), 422 if 'name' in data: user.name = data['name'] if 'email' in data: # Comprobar email único if User.query.filter(User.email == data['email'], User.id != id).first(): return jsonify({'message': 'Email already in use'}), 400 user.email = data['email'] db.session.commit() return user_schema.jsonify(user), 200 |
Paso 6. Ruta para eliminar usuario
1 2 3 4 5 6 7 |
@app.route('/users/<int:id>', methods=['DELETE']) def delete_user(id): user = User.query.get_or_404(id) db.session.delete(user) db.session.commit() return jsonify({'message': 'User deleted successfully'}), 200 |
5. Manejo de Solicitudes y Respuestas JSON
En las rutas anteriores utilizamos request.get_json()
para extraer datos en formato JSON enviados por el cliente. También respondemos con jsonify()
para devolver respuestas JSON adecuadas con sus códigos HTTP correspondientes (200, 201, 400, 404, 422).
Es importante siempre validar que la solicitud contenga datos y que estén en el formato correcto, para evitar errores inesperados.
6. Validación de Datos
Utilizamos Marshmallow para:
- Validar que los campos
name
yemail
sean obligatorios. - Verificar que
email
tenga un formato válido.
Por ejemplo, en la creación de usuarios manejamos excepciones al cargar el esquema para capturar errores de validación y enviar respuestas útiles al cliente.
7. Pruebas Básicas
Finalmente, hagamos pruebas simples con pytest
para validar que nuestra API responde correctamente.
Crear archivo test_app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import pytest from app import app, db, User @pytest.fixture def client(): app.config['TESTING'] = True app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' with app.test_client() as client: with app.app_context(): db.create_all() yield client with app.app_context(): db.drop_all() def test_create_user(client): response = client.post('/users', json={ 'name': 'Test User', 'email': 'test@example.com' }) assert response.status_code == 201 data = response.get_json() assert data['name'] == 'Test User' assert data['email'] == 'test@example.com' def test_get_users_empty(client): response = client.get('/users') assert response.status_code == 200 data = response.get_json() assert data == [] def test_get_user_not_found(client): response = client.get('/users/999') assert response.status_code == 404 |
Ejecutar las pruebas
En la terminal:
1 2 |
pytest |
Verás que las pruebas confirman el correcto funcionamiento básico de la API.
Conclusión y Buenas Prácticas
En este tutorial Flask para principiantes, has aprendido a crear una API RESTful usando Flask y SQLAlchemy, desde configurar el entorno hasta realizar operaciones CRUD y validar datos. Esta base es ideal para ampliar funcionalidades e integrar autenticación o bases de datos más robustas.
Buenas prácticas a considerar
- Siempre validar y sanitizar la entrada de datos.
- Manejar errores con respuestas claras y códigos HTTP adecuados.
- Usar esquemas y serializers (como Marshmallow) para mantener consistencia.
- Separar el código en módulos para facilitar mantenimiento a medida crece el proyecto.
- Escribir pruebas unitarias y funcionales para asegurar calidad.
Próximos pasos recomendados
- Implementar autenticación (ej. JWT) para proteger las rutas.
- Utilizar Base de datos PostgreSQL o MySQL para producción.
- Documentar la API con Swagger o similar.
¡Anímate a seguir explorando y desarrollando tu experiencia en desarrollo backend Flask!
¿Te ha gustado este tutorial?
Comparte y comenta tu experiencia. Si necesitas ayuda o quieres un tema avanzado, ¡no dudes en preguntar!
Código completo app.py
para referencia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from marshmallow import fields app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) ma = Marshmallow(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) def __repr__(self): return f'<User {self.name}>' class UserSchema(ma.SQLAlchemyAutoSchema): class Meta: model = User load_instance = True name = fields.String(required=True) email = fields.Email(required=True) user_schema = UserSchema() users_schema = UserSchema(many=True) @app.route('/users', methods=['GET']) def get_users(): all_users = User.query.all() result = users_schema.dump(all_users) return jsonify(result), 200 @app.route('/users/<int:id>', methods=['GET']) def get_user(id): user = User.query.get_or_404(id) return user_schema.jsonify(user), 200 @app.route('/users', methods=['POST']) def create_user(): json_data = request.get_json() if not json_data: return jsonify({'message': 'No input data provided'}), 400 try: user = user_schema.load(json_data) except Exception as e: return jsonify({'errors': str(e)}), 422 if User.query.filter_by(email=user.email).first(): return jsonify({'message': 'User with this email already exists'}), 400 db.session.add(user) db.session.commit() return user_schema.jsonify(user), 201 @app.route('/users/<int:id>', methods=['PUT']) def update_user(id): user = User.query.get_or_404(id) json_data = request.get_json() if not json_data: return jsonify({'message': 'No input data provided'}), 400 try: data = user_schema.load(json_data, partial=True) except Exception as e: return jsonify({'errors': str(e)}), 422 if 'name' in data: user.name = data['name'] if 'email' in data: if User.query.filter(User.email == data['email'], User.id != id).first(): return jsonify({'message': 'Email already in use'}), 400 user.email = data['email'] db.session.commit() return user_schema.jsonify(user), 200 @app.route('/users/<int:id>', methods=['DELETE']) def delete_user(id): user = User.query.get_or_404(id) db.session.delete(user) db.session.commit() return jsonify({'message': 'User deleted successfully'}), 200 if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True) |
Recuerda: Para más tutoriales API REST Flask, seguir prácticas recomendadas y profundizar en Python backend, ¡mantente atento a nuestras guías actualizadas!