En el mundo del desarrollo web con Python, Flask se ha consolidado como un microframework ágil y potente, ideal para construir aplicaciones web y APIs REST de forma rápida. Cuando necesitas persistir datos de manera sencilla, especialmente en proyectos pequeños o prototipos, SQLite emerge como una opción excepcional. Es una base de datos ligera, basada en archivos, que no requiere un servidor separado, lo que la hace increíblemente fácil de configurar y usar. Combinar Flask y SQLite te permite crear aplicaciones backend funcionales sin la complejidad de bases de datos más grandes. Este artículo te mostrará cómo conectar una base de datos SQLite a tu aplicación Flask y cómo utilizarla para construir una API REST básica.

Construiremos una API sencilla para gestionar usuarios, utilizando las operaciones fundamentales: Crear (POST), Leer (GET), Actualizar (PUT) y Eliminar (DELETE), comúnmente conocidas como operaciones CRUD. Partiremos de la base de tener una base de datos SQLite simple, por ejemplo, un archivo llamado example.db, con una tabla users que contenga al menos las columnas id, name y email.

Preparando el Entorno y la Conexión
Para empezar, necesitas tener Python y Flask instalados. Si aún no lo tienes, puedes instalar Flask usando pip:
pip install Flask
Python ya incluye el módulo sqlite3 por defecto, por lo que no necesitas instalar nada adicional para interactuar con bases de datos SQLite.
El primer paso en tu archivo principal de la aplicación (por ejemplo, app.py) es importar los módulos necesarios de Flask y el módulo sqlite3 para la base de datos:
from flask import Flask, jsonify, request import sqlite3Luego, inicializa tu aplicación Flask:
app = Flask(__name__)La conexión a la base de datos SQLite se realiza utilizando la función sqlite3.connect(). Esta función abre una conexión a un archivo de base de datos SQLite. Si el archivo no existe, lo crea automáticamente. Es fundamental manejar las conexiones y cerrarlas adecuadamente para liberar recursos.
Construyendo los Endpoints de la API REST
Ahora definiremos los diferentes endpoints de nuestra API REST para interactuar con la tabla users. Cada endpoint corresponderá a una operación CRUD.
Endpoint para Obtener Todos los Usuarios (GET /users)
Este endpoint responderá a solicitudes GET en la ruta /users y devolverá una lista de todos los usuarios presentes en la base de datos.
@app.route('/users', methods=['GET']) def get_users(): # Conectar a la base de datos conn = sqlite3.connect('example.db') # Crear un objeto cursor cursor = conn.cursor() # Ejecutar la consulta SQL para obtener todos los usuarios cursor.execute('SELECT * FROM users') users = cursor.fetchall() # Cerrar el cursor y la conexión a la base de datos cursor.close() conn.close() # Retornar los usuarios como JSON return jsonify(users)Analicemos este código. Primero, establecemos la conexión a example.db. Luego, creamos un objeto cursor. Un cursor es una abstracción que permite recorrer los registros de una base de datos. Es a través del cursor que ejecutamos comandos SQL. cursor.execute('SELECT * FROM users') envía la consulta para seleccionar todas las filas de la tabla users. cursor.fetchall() recupera *todas* las filas del resultado de la consulta. Finalmente, y esto es crucial, cerramos el cursor y la conexión para liberar los recursos del sistema asociados con ellos. La función jsonify() de Flask convierte la lista de usuarios (que fetchall devuelve como una lista de tuplas o listas) en una respuesta JSON.
Endpoint para Obtener un Usuario Específico (GET /users/<id>)
Este endpoint permite obtener los detalles de un usuario individual basándose en su ID. La ruta incluye un parámetro variable <int:user_id> que Flask captura y pasa como argumento a la función.
@app.route('/users/<int:user_id>', methods=['GET']) def get_user(user_id): # Conectar a la base de datos conn = sqlite3.connect('example.db') cursor = conn.cursor() # Ejecutar la consulta SQL para obtener el usuario por ID # Usamos '?' como placeholder para evitar inyección SQL cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,)) user = cursor.fetchone() # Cerrar el cursor y la conexión a la base de datos cursor.close() conn.close() # Verificar si el usuario existe y retornarlo if user: return jsonify(user) else: # Retornar un mensaje de error con código 404 si no se encuentra return jsonify({'message': 'User not found'}), 404Similar al endpoint anterior, conectamos y creamos un cursor. La diferencia clave está en la consulta SQL: SELECT * FROM users WHERE id = ?. Usamos un placeholder ? en lugar de concatenar directamente el valor de user_id en la cadena SQL. Esto es una práctica de seguridad fundamental para prevenir ataques de inyección SQL. El valor real para el placeholder se pasa como una tupla (incluso si es un solo valor) como segundo argumento de execute(). cursor.fetchone() recupera solo la siguiente fila del resultado, que en este caso esperamos que sea una (o ninguna) ya que buscamos por ID. Si se encuentra el usuario (user no es None), lo retornamos como JSON; de lo contrario, retornamos un mensaje de error y el código de estado HTTP 404 (Not Found).
Endpoint para Crear un Nuevo Usuario (POST /users)
Este endpoint maneja las solicitudes POST a /users para añadir un nuevo usuario a la base de datos. Los datos del nuevo usuario (nombre y email) se esperan en el cuerpo de la solicitud en formato JSON.
@app.route('/users', methods=['POST']) def create_user(): # Obtener los datos del usuario del cuerpo de la solicitud data = request.get_json() # Validar que los datos necesarios estén presentes (ejemplo básico) if not data or 'name' not in data or 'email' not in data: return jsonify({'message': 'Invalid user data'}), 400 name = data['name'] email = data['email'] # Conectar a la base de datos conn = sqlite3.connect('example.db') cursor = conn.cursor() # Ejecutar la consulta SQL para insertar un nuevo usuario cursor.execute('INSERT INTO users (name, email) VALUES (?, ?)', (name, email)) # Confirmar los cambios en la base de datos conn.commit() # Cerrar el cursor y la conexión a la base de datos cursor.close() conn.close() # Retornar un mensaje de éxito return jsonify({'message': 'User created successfully'}), 201 # Código 201 CreatedAquí, request.get_json() parsea el cuerpo de la solicitud entrante, esperando que sea JSON, y lo convierte en un diccionario Python. Obtenemos el name y el email de este diccionario. La consulta INSERT INTO users (name, email) VALUES (?, ?) inserta una nueva fila. Nuevamente, usamos placeholders ? por seguridad. Después de ejecutar una consulta que modifica la base de datos (INSERT, UPDATE, DELETE), es *esencial* llamar a conn.commit(). Este método guarda los cambios de forma permanente. Si olvidas llamar a commit(), los cambios no se reflejarán en la base de datos. Cerramos las conexiones y retornamos un mensaje de éxito, usando el código de estado HTTP 201 (Created) que es estándar para la creación exitosa de recursos.
Endpoint para Actualizar un Usuario Existente (PUT /users/<id>)
Este endpoint permite modificar los datos de un usuario existente, identificado por su ID en la URL. Los nuevos datos se envían en el cuerpo de la solicitud PUT.
@app.route('/users/<int:user_id>', methods=['PUT']) def update_user(user_id): # Obtener los datos actualizados del cuerpo de la solicitud data = request.get_json() # Validar datos if not data or 'name' not in data or 'email' not in data: return jsonify({'message': 'Invalid update data'}), 400 name = data['name'] email = data['email'] # Conectar a la base de datos conn = sqlite3.connect('example.db') cursor = conn.cursor() # Ejecutar la consulta SQL para actualizar el usuario cursor.execute('UPDATE users SET name = ?, email = ? WHERE id = ?', (name, email, user_id)) # Confirmar los cambios conn.commit() # Verificar si se afectó alguna fila (si el usuario existía) if cursor.rowcount == 0: conn.close() return jsonify({'message': 'User not found'}), 404 # Cerrar el cursor y la conexión cursor.close() conn.close() # Retornar un mensaje de éxito return jsonify({'message': 'User updated successfully'})Similar al endpoint POST, obtenemos los datos del cuerpo JSON. La consulta UPDATE users SET name = ?, email = ? WHERE id = ? actualiza las columnas name y email para la fila cuyo id coincide con el proporcionado. Nuevamente, usamos placeholders y pasamos los valores como una tupla. Es crucial llamar a conn.commit() para que los cambios se guarden. Una buena práctica adicional es verificar si la actualización afectó alguna fila (`cursor.rowcount`). Si rowcount es 0, significa que no se encontró ningún usuario con ese ID, y podemos retornar un 404.
Endpoint para Eliminar un Usuario (DELETE /users/<id>)
Este endpoint permite eliminar un usuario específico basándose en su ID.
@app.route('/users/<int:user_id>', methods=['DELETE']) def delete_user(user_id): # Conectar a la base de datos conn = sqlite3.connect('example.db') cursor = conn.cursor() # Ejecutar la consulta SQL para eliminar el usuario cursor.execute('DELETE FROM users WHERE id = ?', (user_id,)) # Confirmar los cambios conn.commit() # Verificar si se afectó alguna fila (si el usuario existía) if cursor.rowcount == 0: conn.close() return jsonify({'message': 'User not found'}), 404 # Cerrar el cursor y la conexión cursor.close() conn.close() # Retornar un mensaje de éxito return jsonify({'message': 'User deleted successfully'})Este endpoint sigue el mismo patrón: conectar, crear cursor, ejecutar SQL, commit, cerrar. La consulta DELETE FROM users WHERE id = ? elimina la fila con el ID especificado. Al igual que con PUT, verificar cursor.rowcount nos permite saber si se eliminó alguna fila y retornar un 404 si el usuario no existía.
Ejecutando la Aplicación Flask
Finalmente, necesitas agregar el código para que la aplicación Flask se ejecute cuando ejecutas el script Python:
if __name__ == '__main__': app.run(debug=True) # debug=True es útil durante el desarrolloEste bloque asegura que app.run() se llame solo cuando el script se ejecuta directamente (no cuando se importa como un módulo). app.run() inicia el servidor web de desarrollo de Flask.
Consideraciones Adicionales
Es vital recordar la importancia de cerrar siempre las conexiones a la base de datos (conn.close()) y los cursores (cursor.close()) para liberar recursos. Aunque en este ejemplo simple las conexiones se cierran al final de cada función de endpoint, en aplicaciones más complejas, podrías considerar usar bloques try...finally o manejadores de contexto (usando with) para garantizar que las conexiones se cierren incluso si ocurren errores.
El uso de placeholders (?) en las consultas es la forma recomendada de pasar valores a la base de datos con sqlite3, ya que maneja automáticamente el escape de caracteres especiales, protegiéndote contra la inyección SQL.
Resumen de Endpoints de la API
| Método HTTP | Ruta | Descripción | Función Flask |
|---|---|---|---|
| GET | /users | Obtiene todos los usuarios. | get_users |
| GET | /users/<id> | Obtiene un usuario por su ID. | get_user |
| POST | /users | Crea un nuevo usuario. | create_user |
| PUT | /users/<id> | Actualiza un usuario por su ID. | update_user |
| DELETE | /users/<id> | Elimina un usuario por su ID. | delete_user |
Preguntas Frecuentes sobre SQLite y Flask
¿Por qué usar SQLite en lugar de otra base de datos como PostgreSQL o MySQL?
SQLite es ideal para aplicaciones pequeñas, desarrollo rápido, pruebas, o cuando no necesitas un servidor de base de datos separado. Es ligera, no requiere configuración de red o administración de usuarios compleja, y la base de datos es simplemente un archivo en disco. Para aplicaciones grandes o con alta concurrencia, bases de datos cliente-servidor como PostgreSQL o MySQL son generalmente más adecuadas.
¿Qué es un cursor en el módulo sqlite3?
Un cursor es un objeto que te permite ejecutar comandos SQL y navegar por los resultados de una consulta. Actúa como un puntero o manejador para interactuar con la base de datos a través de una conexión establecida.
¿Por qué necesito llamar a conn.commit() después de INSERT, UPDATE o DELETE?
Las operaciones que modifican la base de datos (INSERT, UPDATE, DELETE) se ejecutan dentro de una transacción. conn.commit() finaliza la transacción y guarda los cambios de forma permanente en el archivo de base de datos. Si no llamas a commit(), los cambios se perderán cuando la conexión se cierre (a menos que la conexión esté en modo autocommit, lo cual no es el predeterminado y no se recomienda para operaciones de escritura).
¿Qué hace jsonify()?
jsonify() es una función de Flask que serializa un objeto Python (como un diccionario o una lista) a formato JSON y crea una respuesta de Flask con el encabezado Content-Type establecido a application/json. Es la forma estándar en Flask de devolver datos estructurados desde una API.
¿Es seguro usar sqlite3 directamente en Flask para una API pública?
Sí, es seguro en cuanto a la prevención de inyección SQL si usas placeholders (?) como se mostró. Sin embargo, para aplicaciones con mucha concurrencia, SQLite puede tener limitaciones de rendimiento y bloqueo. Además, la gestión de la conexión por solicitud simple como en este ejemplo podría no ser la más eficiente para altas cargas. Para producción con tráfico significativo, se suelen usar ORMs como SQLAlchemy que gestionan mejor las conexiones, o bases de datos cliente-servidor.
Conectar SQLite a Flask es un proceso directo que abre un mundo de posibilidades para tus proyectos Python. Siguiendo los pasos y entendiendo los conceptos básicos de conexión, cursor y commit, puedes construir APIs REST funcionales y eficientes para manejar tus datos de forma local o en aplicaciones de pequeña escala. Este ejemplo básico de API CRUD es un punto de partida sólido para desarrollar aplicaciones web más complejas.
Si quieres conocer otros artículos parecidos a SQLite y Flask: Conexión para API REST puedes visitar la categoría Programación.

Aprende mas sobre MySQL