<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eduardo Santos</title>
    <description>The latest articles on DEV Community by Eduardo Santos (@edsantoshn).</description>
    <link>https://dev.to/edsantoshn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F23875%2Fbd058625-e7f6-4bac-a169-3305ffaf368b.JPG</url>
      <title>DEV Community: Eduardo Santos</title>
      <link>https://dev.to/edsantoshn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edsantoshn"/>
    <language>en</language>
    <item>
      <title>Construyendo APIs Serverless Resilientes: Webhook Gateway con Amazon EventBridge</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Sat, 22 Feb 2025 17:51:28 +0000</pubDate>
      <link>https://dev.to/edsantoshn/construyendo-apis-serverless-resilientes-webhook-gateway-con-amazon-eventbridge-4l8a</link>
      <guid>https://dev.to/edsantoshn/construyendo-apis-serverless-resilientes-webhook-gateway-con-amazon-eventbridge-4l8a</guid>
      <description>&lt;p&gt;Las arquitecturas distribuidas tienen un componente fundamental, el webhook se convirtió en ese componente para la integración de sistemas en tiempo real. Sin embargo, representa varios desafíos: ¿Cómo manejamos los picos de tráfico?, ¿Cómo garantizamos la entrega confiable de los mensajes?, ¿Qué sucede cuando los sistemas downstream están caídos?&lt;/p&gt;

&lt;p&gt;Exploraremos como construir un webhook serverless utilizando los servicios de AWS, que no solo resuelve los desafíos anteriormente listados, sino que también nos proporciona escalabilidad y tolerancia a los fallos.&lt;/p&gt;

&lt;p&gt;Es importante mencionar que con la propuesta de esta arquitectura permitiremos que no se sobrepase el soft limit de la ejecución de lambdas que tiene por defecto AWS (1000 funciones lambdas ejecutándose al mismo tiempo).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué una arquitectura Serverless para webhooks?&lt;/strong&gt;&lt;br&gt;
Los webhooks por su naturaleza son de trafico impredecible, por lo cual, las arquitecturas Serverless son particularmente idóneas para este caso de uso. Asimismo, no es necesario mantener servidores a la espera de eventos para manejar los picos ocasionales de tráfico, y solo pagamos por lo que usamos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arquitectura por desarrollar:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yfe9cc9r3s8x6xdi8vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yfe9cc9r3s8x6xdi8vo.png" alt="Image description" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; API Gateway endpoint: Validara el payload y preparara los headers necesarios para llamar al EventBridge Bus.&lt;/li&gt;
&lt;li&gt; EventBridge Bus: Distribuye los eventos enviados por el API Gateway hacia las reglas de eventbridge.&lt;/li&gt;
&lt;li&gt; EventBridge Rule: Se evalúa cada evento recibido desde el EB Bus, y se determinan hacia donde debe de enviarse basado en los patrones del evento.&lt;/li&gt;
&lt;li&gt; Cola SQS Standard: Actúa como un buffer y garantiza el procesamiento de mensajes y el manejo de picos de tráficos.&lt;/li&gt;
&lt;li&gt; DLQ SQS Standard: Captura los eventos que fallan después de 3 reintentos, permite el análisis posterior de eventos fallidos y con posibilidad de reprocesamiento manual si es necesario.&lt;/li&gt;
&lt;li&gt; Función Lambda: Procesa los mensajes de la cola SQS de forma asíncrona y escalable de los eventos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Explicación del código&lt;/strong&gt;&lt;br&gt;
API Gateway endpoint&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frn4yztj3e6p0mkptowux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frn4yztj3e6p0mkptowux.png" alt="Image description" width="800" height="544"&gt;&lt;/a&gt;&lt;br&gt;
Source: Este campo se valida como prefijo en las reglas de eventbridge para saber cuales deben dejar pasar el evento. &lt;/p&gt;

&lt;p&gt;EventBridge Rule&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek3r1zk52nuafjoujksu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek3r1zk52nuafjoujksu.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suscripcion Función Lambda&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfgjgv5ys3zoi3o789ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfgjgv5ys3zoi3o789ch.png" alt="Image description" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beneficios de esta Arquitectura&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Resiliencia: El uso de SQS y DLQ garantiza que ningún mensaje se pierda&lt;/li&gt;
&lt;li&gt; Escalabilidad: La arquitectura serverless escala automáticamente&lt;/li&gt;
&lt;li&gt; Control de Costos: Solo pagamos por el procesamiento real&lt;/li&gt;
&lt;li&gt; Mantenibilidad: Cada componente tiene una responsabilidad única&lt;/li&gt;
&lt;li&gt; Observabilidad: Fácil monitoreo mediante CloudWatch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El código fuente completo lo podrás encontrar en el siguiente enlace: &lt;a href="https://github.com/edsantoshn/serverless-webhook-eventbridge-gateway" rel="noopener noreferrer"&gt;Repositorio de Github&lt;/a&gt; &lt;br&gt;
Para el despliegue completo de esta solución se requiere tener:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un bucket donde almacenar los stacks anidados.&lt;/li&gt;
&lt;li&gt;Un parameter store con el nombre del bucket.&lt;/li&gt;
&lt;li&gt;Los secretos necesarios de AWS para poder desplegar la solución.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>webhook</category>
      <category>serverless</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Consumiendo Certificados de Secrets Manager en Ambientes Serverless</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Sat, 21 Sep 2024 20:49:55 +0000</pubDate>
      <link>https://dev.to/edsantoshn/consumiendo-certificados-de-secrets-manager-en-ambientes-serverless-12aa</link>
      <guid>https://dev.to/edsantoshn/consumiendo-certificados-de-secrets-manager-en-ambientes-serverless-12aa</guid>
      <description>&lt;p&gt;Alguna vez has tenido la necesidad de utilizar certificados dentro de tu Lambda para poder autenticar peticiones que realizas a servidores o consumo de servicios externos?&lt;/p&gt;

&lt;p&gt;Como desarrolladores cada día nos enfrentamos a diferentes retos que nos motivan a mejorar nuestra arquitectura o procesos internos para aplicar mejores practicas. Trabajar con certificados es uno de esos retos que podemos enfrentar en el día a día , y estos a su vez con los diferentes retos de seguridad, algunos prefieren incluir los certificados como archivos en el despliegue de la Lambda que es una opción valida pero que en el caso que el certificado venza o se desactive por lo solicitud del cliente o el proveedor del certificado, esto requerirá desplegar de nuevo todas las lambdas  o funciones que utilicen dicho certificado.&lt;/p&gt;

&lt;p&gt;Para hacer frente al escenario anterior, podemos utilizar un servicio de AWS Secrets Manager nos permite almacenar nuestros secretos (incluyendo certificados) en la nube de AWS y ser consumidos por los servicios como ser Lambdas, Glue, etc.&lt;/p&gt;

&lt;p&gt;Ventajas:&lt;br&gt;
Rotación de certificados de forma centralizada.&lt;br&gt;
Replican de certificados en diferentes regiones.&lt;br&gt;
Desacoplamiento de nuestros secretos con nuestras funciones.&lt;br&gt;
Posibilidad de crear nuevos servicios que consuman los certificados.&lt;/p&gt;

&lt;p&gt;Escenario:&lt;br&gt;
Debes de conectarte a una API de tercero que requiere que se autentique el usuario con certificados como PFX, CRT y KEY. Los certificados son asignados a un empleado que puede ser rotado dentro de la empresa, por lo cual, los certificados pueden cambiar sin haber expirado. Antes que se expiren los certificados el ente emisor emitirá los nuevos certificados para que el equipo de ingeniería realice los cambios necesarios en sus sistemas.&lt;/p&gt;

&lt;p&gt;Solución 1&lt;br&gt;
Adjuntar los certificados en cada una de las diferentes Lambdas.&lt;br&gt;
Pros:&lt;br&gt;
Los certificados no pueden ser accedidos, desde fuera de las Lambdas que los contienen.&lt;/p&gt;

&lt;p&gt;Contras:&lt;br&gt;
Al cambiar el certificado sera necesario volver a desplegar todas las funciones donde se requiera el uso de los certificados. Asimismo, cada nuevo servicio requerirá que se adjunte los certificados cuando sean necesarios.&lt;/p&gt;

&lt;p&gt;Solución 2&lt;br&gt;
Almacenar los certificados en Secrets Manager y consumirlos en las Lambdas que lo requieran.&lt;br&gt;
Pros: &lt;br&gt;
Cifrado de los certificados en reposo&lt;br&gt;
Rotación de certificados sin necesidad de afectar las funciones que lo necesitan.&lt;br&gt;
Los certificados podrán ser almacenados de forma temporal en la memoria efímera de nuestra lambda, reduciendo la necesidad de extraerlos constantemente de Secrets Manager.&lt;/p&gt;

&lt;p&gt;Contras:&lt;br&gt;
Si no se tiene una política de usuarios bien definida donde se limiten los accesos completos a los diferentes servicios, cualquier usuario con acceso a la consola de AWS podría obtener los certificados.&lt;/p&gt;

&lt;p&gt;Por las ventajas que ofrece la segunda opción obtendremos por desarrollarla, nuestra arquitectura quedaría de la siguiente forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5ou6mr0uoo6r7tdtr81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5ou6mr0uoo6r7tdtr81.png" alt="Image description" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podrás encontrar el repositorio del código a la solución 2 en el siguiente enlace &lt;a href="https://github.com/edsantoshn/aws-secrets-manager" rel="noopener noreferrer"&gt;https://github.com/edsantoshn/aws-secrets-manager&lt;/a&gt; &lt;/p&gt;

</description>
      <category>aws</category>
      <category>secretmanager</category>
      <category>python</category>
      <category>boto3</category>
    </item>
    <item>
      <title>¿Qué es más rápido y económico para convertir archivos en AWS: Polar o Pandas?</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Sat, 10 Aug 2024 21:51:13 +0000</pubDate>
      <link>https://dev.to/edsantoshn/que-es-mas-rapido-y-economico-para-convertir-archivos-en-aws-polar-o-pandas-594p</link>
      <guid>https://dev.to/edsantoshn/que-es-mas-rapido-y-economico-para-convertir-archivos-en-aws-polar-o-pandas-594p</guid>
      <description>&lt;p&gt;Ambas ofrecen una amplia gama de herramientas y ventajas que nos pueden poner en duda cual de los dos escoger en algún momento. No se trata de cambiar todos los procesos de la empresa para que inicien a utilizar Polars o una “muerte” a Pandas (esto no va a pasar en el futuro inmediato). Se trata de conocer otras herramientas que nos pueden ayudar a reducir costos y tiempo en los procesos obteniendo iguales o mejores resultados.&lt;/p&gt;

&lt;p&gt;Cuando utilizamos servicios en la nube priorizamos ciertos factores, dentro de los que se encuentran el costo de los mismos. Los servicios que utilizo para este proceso son AWS Lambda con el runtime de Python 3.10 y S3 para almacenar el archivo crudo y el archivo convertido en parquet.&lt;/p&gt;

&lt;p&gt;La intención es obtener un archivo CSV como data cruda y procesarlo con pandas y polars con la intención de verificar cual de estas dos bibliotecas nos ofrece una mejor optimización de recursos como memoria y el peso del archivo resultante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pandas&lt;/strong&gt;&lt;br&gt;
Es una biblioteca de Python especializada en la manipulación y análisis de datos, esta escrito en C y su lanzamiento inicial fue en el 2008.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Polars *&lt;/em&gt;&lt;br&gt;
Es una biblioteca de Python y Rust especializada en la manipulación y análisis de datos que permite procesos paralelos y está escrito mayormente en Rust y su lanzamiento fue en el 2022.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La arquitectura del proceso:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhrh86lw5weycvl1swhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhrh86lw5weycvl1swhb.png" alt="Image description" width="567" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El proyecto es algo sencillo como se muestra en la arquitectura: El usuario deposita un archivo CSV en work/pandas o work/porlas y automáticamente inicia el s3 trigger a procesar el archivo para convertirlo en parquet y depositarlo en processed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;En este pequeño proyecto utilice dos lambdas con la siguiente configuración:&lt;/strong&gt;&lt;br&gt;
Memoria: 2 GB&lt;br&gt;
Memoria efímera: 2 GB&lt;br&gt;
Tiempo de vida: 600 segundos&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requerimientos&lt;/strong&gt;&lt;br&gt;
Lambda con pandas: Pandas, Numpy y Pyarrow&lt;br&gt;
Lambda con polars: Polars&lt;/p&gt;

&lt;p&gt;El dataset utilizado para la comparación esta disponible en kaggle con el nombre de “Rotten Tomatoes Movie Reviews – 1.44M rows” o puede ser descargado desde &lt;a href="https://www.kaggle.com/datasets/priyamchoksi/rotten-tomato-movie-reviews-1-44m-rows" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El repositorio completo está disponible en GitHub puede ser clonado &lt;a href="https://github.com/edsantoshn/pandas_vs_polars" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamaño o Peso&lt;/strong&gt;&lt;br&gt;
La lambda que utilizada Pandas requiere dos complementos mas para poder crear un archivo parquet en este caso es PyArrow y una versión en especifico de numpy para la versión de pandas que estaba utilizando. Como resultado obtuvimos una lambda con un peso o tamaño de 74.4 MB algo muy cercano al límite que nos permite AWS de peso de la lambda.&lt;/p&gt;

&lt;p&gt;La lambda con Polars no requiere otro complemento como PyArrow lo que simplifica la vida y reduce el tamaño de la lambda a menos de la mitad. Como resultado nuestra lambda tiene un peso o tamaño de 30.6 MB comparado con la primera nos da una amplitud para instalar otras dependencias que podremos necesitar para nuestro proceso de transformación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftihaf1i62nyi9e9m9x9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftihaf1i62nyi9e9m9x9k.png" alt="Image description" width="567" height="336"&gt;&lt;/a&gt;&lt;br&gt;
La lambda con Pandas fue optimizada para utilizar compresión luego de la primera versión, sin embargo, se analizó también su comportamiento.&lt;br&gt;
Pandas&lt;br&gt;
Se tardo 18 segundos en procesar el dataset y utilizo 1894 MB de memoria para procesar el archivo CSV y generar un archivo Parquet en comparación con las otras versiones fue la que mas tiempo y recursos utilizo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pandas + Compression&lt;/strong&gt;&lt;br&gt;
Agregando una línea de código permitió mejorar un poco comparada con la versión anterior (Pandas), se tardo 17 segundos en procesar el dataset y utilizo 1837 MB, lo cual, no representa una mejora significativa en procesamiento y tiempo computacional, pero si en el tamaño del archivo resultante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polars&lt;/strong&gt;&lt;br&gt;
Se tardo 12 segundos en procesar el mismo dataset y utilizo solamente 1462 MB, en comparación con las dos anteriores representa un ahorro de tiempo del 44.44% y un consumo de memoria inferior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamaño del archivo de salida&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6yhiiz9o7fjkvdkt6un.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6yhiiz9o7fjkvdkt6un.png" alt="Image description" width="567" height="341"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Pandas&lt;/strong&gt;&lt;br&gt;
La lambda en la cual no se estableció un proceso de compresión nos genero un archivo parquet de 177.4 MB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pandas + Compression&lt;/strong&gt;&lt;br&gt;
Al configurar la compresión en la lambda no genero un archivo parquet de 121.1 MB. Una pequeña línea u opción nos ayudo a reducir el tamaño del archivo en un 31.74%. Considerando que no es un cambio significativo de código es una muy buena opción.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polars&lt;/strong&gt;&lt;br&gt;
Polars nos genero un archivo de 105.8 MB que comprado con la primera versión de pandas representa un ahorro del 40.36% y un 12.63% contra la versión pandas con compression. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;br&gt;
No es necesario cambiar todos los procesos internos que utilizan Pandas para que ahora utilicen Polars, sin embargo, es importante considerar que si hablamos de miles o millones de ejecuciones lambdas utilizar Polars nos ayudara no solo con el tiempo de despliegue si no que nos ayudara a tener un costo menor debido al cobro por tiempo que realiza AWS en los servicios Serverless como lo es Lambda.&lt;br&gt;
Asimismo, cuando traducimos ese 40.36% en millones de archivo estamos hablando de GBs o TBs, algo que si tendría un impacto significativo dentro de Datalake o Dataware house o incluso en un almacen de archivos en frio.&lt;/p&gt;

&lt;p&gt;La reducción con Polars no solo se limitaría a estos dos factores, porque afectaría mucho la salida de datos y/o objetos de AWS porque es un servicio que si tiene un costo.&lt;/p&gt;

</description>
      <category>python</category>
      <category>polars</category>
      <category>aws</category>
      <category>pandas</category>
    </item>
    <item>
      <title>Consulta Select en SQLAlchemy</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Fri, 21 Oct 2022 02:37:59 +0000</pubDate>
      <link>https://dev.to/edsantoshn/consulta-select-en-sqlalchemy-3jlf</link>
      <guid>https://dev.to/edsantoshn/consulta-select-en-sqlalchemy-3jlf</guid>
      <description>&lt;p&gt;Seguimos con la serie de conectarnos a una base de datos mediante SQLAlchemy, en este caso utilizare variables de entorno almacenadas en un archivo .env para facilitar la distribución de nuestro programa y mejorar un poco la seguridad de nuestro servidor de base de datos.&lt;/p&gt;

&lt;p&gt;Una excelente practica es modularizar nuestro código dejando un modulo para la conexión de la base de datos, otro modulo con los modelos o clases de la base de datos y las queries.&lt;/p&gt;

&lt;p&gt;Iniciaremos creando el modulo de la conexión de la base de datos, el cual nombrare server.py y contendrá el siguiente código:&lt;br&gt;
import os&lt;/p&gt;

&lt;p&gt;from dotenv import load_dotenv&lt;br&gt;
from sqlalchemy import create_engine&lt;/p&gt;

&lt;h1&gt;
  
  
  lee las variables de entorno
&lt;/h1&gt;

&lt;p&gt;load_dotenv()&lt;/p&gt;

&lt;p&gt;def create_engine_data()-&amp;gt;"create_engine":&lt;br&gt;
    ENGINE_DB = os.getenv('ENGINE_DB')&lt;br&gt;
    CONNECTOR = os.getenv('CONNECTOR')&lt;br&gt;
    USER_DB = os.getenv('USER_DB')&lt;br&gt;
    PSW = os.getenv('PSW')&lt;br&gt;
    IP_SERVER = os.getenv('IP_SERVER')&lt;br&gt;
    DATABASE = os.getenv('DATABASE')&lt;br&gt;
    try:&lt;br&gt;
        engine = create_engine(&lt;br&gt;
                            f'{ENGINE_DB}+{CONNECTOR}://{USER_DB}:\&lt;br&gt;
                                {PSW}@{IP_SERVER}/{DATABASE}')&lt;br&gt;
    except Exception as ex:&lt;br&gt;
        raise ex&lt;br&gt;
    return engine&lt;/p&gt;

&lt;p&gt;Importamos las variables de entorno con el uso de os.getenv, el nombre de cada constante es como se enuentra en nuestro archivo .env, luego procedemos a tratar de crear una instancia del motor de base de datos en el caso que la conexión se logre retornamos la instancia y si existe algún tipo de problema al momento de realizar la conexión nos levantaría una excepción.&lt;/p&gt;

&lt;p&gt;Nota: Si están trabajando con un programa un poco mas complejo, lo adecuado seria importar el dontev y cargar los dotenv al inicio del programa y no en el archivo server.py  quedando las importaciones con dos líneas menos.&lt;/p&gt;

&lt;p&gt;import os&lt;/p&gt;

&lt;p&gt;from sqlalchemy import create_engine&lt;/p&gt;

&lt;h1&gt;
  
  
  Fin de las importaciones
&lt;/h1&gt;

&lt;h1&gt;
  
  
  La función se mantiene intacta
&lt;/h1&gt;

&lt;p&gt;Con el modulo de conexión al servidor de base de datos creado, podemos crear el modulo o los modelos de la base de datos que necesitaremos. Es importante aclarar que no es necesario modelar toda la base de datos, si solo necesitamos dos o tres tablas para trabajar basta con modelar estas tablas, asimismo no es necesario modelar toda la tabla si vamos a trabajar con 2 o 3 campos podemos modelar solamente esos campos.&lt;/p&gt;

&lt;p&gt;Nuestra tabla usuarios tiene los siguientes campos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4n6fw6mr9hho9uj0vik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4n6fw6mr9hho9uj0vik.png" alt="Image description" width="698" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A modo de ejemplo dejare por fuera el campo telephone, solamente porque no lo necesitaría.&lt;/p&gt;

&lt;p&gt;El código de nuestro modelo de la tabla seria como el siguiente:&lt;br&gt;
from sqlalchemy import Column&lt;br&gt;
from sqlalchemy import String&lt;br&gt;
from sqlalchemy.orm import declarative_base&lt;/p&gt;

&lt;p&gt;Base = declarative_base()&lt;/p&gt;

&lt;p&gt;class Usuario(Base):&lt;br&gt;
    #Nombre de la tabla en la base de datos&lt;br&gt;
    &lt;strong&gt;tablename&lt;/strong&gt; = "usuarios"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;username = Column(String(30), primary_key=True)
name = Column(String(50))
surname = Column(String(50))
email = Column(String(60), unique=True)

#Representacion para el programador
def __repr__(self):
    return f"Usuario(username={self.username!r}, name={self.name!r},\
        surname={self.surname!r}, email={self.email!r})"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Con nuestro modelo hecho podemos continuar con el proceso de acceder a los datos en nuestra base de datos mediante el ORM, debemos crear la consulta o query con el ORM, en este caso hare dos consultas una que obtenga todos los usuarios y otra que obtenga los usuarios que cumplen con un criterio en especifico.&lt;/p&gt;

&lt;p&gt;from sqlalchemy import select&lt;br&gt;
from sqlalchemy.orm import Session&lt;/p&gt;

&lt;p&gt;from server import create_engine_data&lt;br&gt;
from models import Usuario&lt;/p&gt;

&lt;p&gt;ENGINE = create_engine_data()&lt;br&gt;
SESSION = Session(ENGINE)&lt;/p&gt;

&lt;p&gt;def obtener_usuarios(name=''):&lt;br&gt;
    if name == '':&lt;br&gt;
        stmt = select(Usuario)&lt;br&gt;
    else:&lt;br&gt;
        stmt = select(Usuario).where(Usuario.name==name)&lt;br&gt;
    for usuario in SESSION.scalars(stmt):&lt;br&gt;
        print(usuario)&lt;/p&gt;

&lt;p&gt;La consulta SQL para extraer todos lo usuarios seria &lt;br&gt;
SELECT username, name, surname, telephone FROM usuarios&lt;/p&gt;

&lt;p&gt;Y La segunda consulta SQL para extraer los usuarios que cumplen con un criterio seria&lt;br&gt;
SELECT username, name, surname, telephone FROM usuario where name = name&lt;/p&gt;

&lt;p&gt;Si deseamos ejecutar el código SQL de forma directa con ayuda de SQLAlchemy podemos hacerlo con ayuda de la instancia del servidor:&lt;/p&gt;

&lt;p&gt;from server import create_engine_data&lt;br&gt;
with ENGINE.connect() as conn:&lt;br&gt;
    data = conn.execute("SELECT username, name, surname, telephone FROM usuarios")&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for usuario in data:
    print(usuario)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Nota: Debemos tener en cuenta que si migramos de motor de base de datos seria mas dificultoso el proceso de migración ya que deberíamos de verificar y reestructurar cada una de las sentencias.&lt;/p&gt;

</description>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SQLAlchemy Intro</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Wed, 19 Oct 2022 22:47:09 +0000</pubDate>
      <link>https://dev.to/edsantoshn/sqlalchemy-intro-467c</link>
      <guid>https://dev.to/edsantoshn/sqlalchemy-intro-467c</guid>
      <description>&lt;p&gt;SQLAlchemy es una librería que facilita la comunicación entre programas hecho en Python y base de datos. Mayormente es utilizada como una ORM (Object Relational Mapper) que traduce las clases de Python a tablas en base de datos relacionales y que automáticamente convierte las funciones utilizadas a sentencias SQL. SQLAlchemy como la mayoría de herramientas provee una interfaz estándar que nos permite como desarrolladores crear la estructura y código de forma agnóstica del motor de base de datos a comunicarse, esto nos permite migrar de un motor a otro solo cambiando la cadena de conexión.&lt;/p&gt;

&lt;p&gt;SQLAlchemy interactúa de forma directa con la API que integra la Base de datos de forma directa permitiéndonos utilizar funciones como connect, close, commit, rollback etc. Para cada motor de base de datos debemos de instalar el DBAPI que mas se adapte a nuestras necesidades para conectarnos a la base de datos.&lt;/p&gt;

&lt;p&gt;Siempre que deseemos usar SQLAlchemy para interactuar con una base de datos, necesitamos crear una instancia de Engine (motor), esta instancia es la encargada de administrar dos factores cruciales Pools y Dialects. &lt;/p&gt;

&lt;p&gt;Para crear la instancia del motor de base de datos utilizamos el siguiente código:&lt;/p&gt;

&lt;p&gt;from sqlalchemy import create_engine&lt;/p&gt;

&lt;p&gt;engine = create_engine(f’{motor_DB}://{user}:{pass}@{dirección_server}:{puerto}/{base_datos}’)&lt;/p&gt;

&lt;p&gt;La estructura anterior tiene variables que pueden ser extraídas de un archivo .env para mejorar el proceso de desplegué del programa. En algunos casos será necesario pasarle el parámetro del driver o un conector ODBC.&lt;/p&gt;

&lt;p&gt;Las conexiones agrupadas son la implementación mas utilizada en el patrón de grupos de objetos, estos grupos son utilizados como objetos pre inicializados en cache para que estén listos para su uso. En lugar de invertir tiempo creando objetos que son utilizados frecuentemente el programa utiliza un objeto preexistente. La principal razón para utilizar este patron de diseño es para mejorar el performance. Con las conexiones a base de datos el tiempo consumido y la cantidad de recursos mal utilizados volverían nuestra aplicación lenta y pesa para cualquier servidor.&lt;/p&gt;

&lt;p&gt;SQLAlchemy Dialects nos permite crear aplicaciones que se comunican con diferentes motores de base de datos en la misma API. Los motores de base de datos mas populares han agregado el uso de SQL dentro de dialects, sin embargo, esto en algunas ocasiones puede generar un serio problema debido a que el código SQL no es un estándar entre los diferentes motores de base de datos, ya que estos últimos han personalizado la sintaxis para obtener los mismos resultados. &lt;/p&gt;

&lt;p&gt;Para evitar que la migración de un motor a otro genere cambios significativos en nuestro código, se recomienda hacer uso del ORM como tal, crear la conexión de base de datos, crear los modelos de las tablas y los queries necesarios, con esto podremos asegurarnos que nuestra aplicación podrá mudarse de motor de base de datos con los cambios mínimos.&lt;/p&gt;

&lt;p&gt;En el próximo post estaremos creando un ejemplo donde se incluyan el código SQL y una consulta con el ORM.&lt;/p&gt;

</description>
      <category>sqlalchemy</category>
      <category>python</category>
      <category>database</category>
    </item>
    <item>
      <title>Consumir datos de SQL Server en Python</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Tue, 18 Oct 2022 03:06:54 +0000</pubDate>
      <link>https://dev.to/edsantoshn/consumir-datos-de-sql-server-en-python-1ibm</link>
      <guid>https://dev.to/edsantoshn/consumir-datos-de-sql-server-en-python-1ibm</guid>
      <description>&lt;p&gt;En algún momento nos hemos encontrado con la necesidad de consumir datos o comunicarnos con una base de datos en SQL Server desde Django, a pesar que de forma nativa no tenemos soporte, te dejo dos opciones que me han ayudado a realizar dicha tarea. A pesar que existen diferentes herramientas es importante tener en cuenta que es lo que realmente necesitamos por ejemplo un ORM como SQLAlchemy o Peewee o un conector odbc como Pyodbc.&lt;/p&gt;

&lt;p&gt;Primero si solo necesitas consumir datos mediante consulta sql o realizar pequeñas modificaciones te recomiendo Pyodbc, este conector no solo te funciona para SQL Server, puedes utilizarlo con MySql, Access, Oracle, Postgresql etc. Lograr conectar tu base de datos mediante ODBC es bastante fácil como veras a continuación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pyodbc
connection = pyodbc.connect(f'''{SU_DRIVER};SERVER={ip_server};port=1433;
                                   DATABASE={db_name};UID={user_name};
                                   PWD={password};Mars_Connection=Yes;
                                   TrustServerCertificate=yes;''')
cursor =  connection.cursor()

#Consulta para obtener todos los usuarios de la base de datos
consulta = f"""select * from users”””
cursor.execute(consulta)
data = cursor.fetchall()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el segundo escenario necesitamos herramientas mas potentes y ahí es donde los ORMs como SQLAlchemy y Peewee nos ayudan en nuestra tarea de optimizar consultas, ya sea que necesitamos que nuestra aplicación consuma datos SQL Server o que la base de datos de nuestro proyecto se encuentre en dicho motor de base de datos, SQLAlchemy es el ORM que prefieren la gran mayoría de empresas para trabajar con datos no solamente SQL Server si no con MySql, Mariadb, Oracle, Postgresql, Firebird etc. &lt;/p&gt;

&lt;p&gt;SQLAlchemy es un Mapeador Relacional de Objetos ORM (por sus siglas en ingles) esto nos permite crear clases como Producto y Orden y que ambos tengan una lista relacional de instancias, a diferencia de Pyodbc para aprovechar todo el poder de la herramienta debemos de crear clases y establecer las relaciones entre ellas, si existen atributos que no son necesarios para nosotros no es necesario definirlos en el modelo (de momento). El siguiente código es un ejemplo para definir la tabla Users y establecer una conexión con la base de datos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sqlalchemy import select
from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import Session
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import relationship

#Linea de conexion
engine = create_engine( f'mssql+pyodbc://{user}:{password} @{ip_server}/{base_datos}?Driver={driver} ',
                            connect_args = {
                                            'TrustServerCertificate':'yes',
                                            'Mars_Connection':'yes'
                                            })

Base = declarative_base()

#Creamos la instancia de la sesión de nuestro servidor
session = Session(engine)

#Definimos la clase usuario con los atributos que necesitamos
class User(Base):
    __tablename__ = "users"

    user_name = Column(String(15), primary_key=True)
    name = Column(String(30))
    email = Column(String(100))

    #Representacion del objeto
    def __repr__(self):
        return f"User(user_name={self.user_name!r}, name={self.name!r}”

#Realiza la consulta sql 
stmt = select(User)

for user in session.scalars(stmt):
    print(user)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A pesar que el código tiene mas líneas que la versión de Pyodbc, con solo cambiar la línea de conexión a otro motor de base de datos nuestro código y consultas seguirían funcionando de la misma forma.&lt;br&gt;
Estaré escribiendo mas sobre SQLAlchemy en los siguientes posts.&lt;/p&gt;

</description>
      <category>pyhton</category>
      <category>sqlalchemy</category>
      <category>sqlserver</category>
    </item>
    <item>
      <title>Optimizar sentencia if, elif, else</title>
      <dc:creator>Eduardo Santos</dc:creator>
      <pubDate>Sun, 16 Oct 2022 20:57:53 +0000</pubDate>
      <link>https://dev.to/edsantoshn/optimizar-sentencia-if-elif-else-3j0</link>
      <guid>https://dev.to/edsantoshn/optimizar-sentencia-if-elif-else-3j0</guid>
      <description>&lt;p&gt;En algún momento de nuestra vida como desarrolladores nos enfrentamos a controlar una situación con diferentes opciones, una de ellas podría ser controlar los días de la semana o los meses.&lt;br&gt;
Para poder enfrentar esta situación escribimos un código como el siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if dia == 1:
    return “lunes”
elif dia == 2:
    return “martes”
…
elif dia == 7:
    return “domingo”
else:
    return “dia no controlado”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero podríamos eficientar este proceso haciendo uso de diccionarios, esto nos permitirá reducir significativamente las líneas de código, y generar un código mucho mas limpio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DICT_DIAS = {
    '1': 'lunes',
    '2': 'martes',
    '3': 'miercoles',
    '4': 'jueves',
    '5': 'viernes',
    '6': 'sabado',
    '7': 'domingo'
}

def obtener_dia(valor:str)-&amp;gt;str:
    return DICT_DIAS.get(valor,'Valor no controlado')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con el código anterior no solamente logramos optimizar nuestro código si no que emula la función de la sentencia case, en caso que el valor buscado no exista devolvera 'Valor no controlado'&lt;/p&gt;

</description>
      <category>python</category>
    </item>
  </channel>
</rss>
