<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Jorge Tech]]></title><description><![CDATA[Bienvenido a mi Blog donde compartiremos ideas, tutoriales, Proof Of Concepts y datos acerca del Desarrollo Web y sobre mi vida como desarrollador.]]></description><link>https://jorgetech.dev/</link><image><url>https://jorgetech.dev/favicon.png</url><title>Jorge Tech</title><link>https://jorgetech.dev/</link></image><generator>Ghost 5.80</generator><lastBuildDate>Thu, 23 Apr 2026 00:09:23 GMT</lastBuildDate><atom:link href="https://jorgetech.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[React.js no es React Native (¿o al revés?)]]></title><description><![CDATA[<p>Si queremos saber o buscamos en Google cu&#xE1;l es la diferencia entre React Native y React.js, encontraremos un mont&#xF3;n de p&#xE1;ginas y posts que establecen la diferencia a nivel t&#xE9;cnico, o dicho de otra manera, las detalles, los protocolos y otras</p>]]></description><link>https://jorgetech.dev/react-js-no-es-react-native/</link><guid isPermaLink="false">689b44119fea8903fd07ebae</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Tue, 12 Aug 2025 14:21:47 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2025/08/feature-1000-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2025/08/feature-1000-1.png" alt="React.js no es React Native (&#xBF;o al rev&#xE9;s?)"><p>Si queremos saber o buscamos en Google cu&#xE1;l es la diferencia entre React Native y React.js, encontraremos un mont&#xF3;n de p&#xE1;ginas y posts que establecen la diferencia a nivel t&#xE9;cnico, o dicho de otra manera, las detalles, los protocolos y otras micro diferencias, sin hablar a grandes rasgos.</p><p>Para un lector no t&#xE9;cnico, estos posts no indican absolutamente nada, s&#xF3;lo mucho texto acerca de cosas HTML, CSS, renderizado, componentes, integraciones, natividad y otras &quot;buzz words&quot; que no dicen nada.</p><p>Entonces quiero colocar y dejar ac&#xE1; una de las principales diferencias que no necesariamente conseguimos en todos los posts, para espec&#xED;ficamente dejarle saber a todos esos &quot;tech head hunters&quot; que se me han acercado con alguna oferta en React en mente:</p><blockquote><strong><mark>React.js es una tecnolog&#xED;a para construir aplicativos web (frontend para p&#xE1;ginas web) y React Native es una tecnolog&#xED;a para construir aplicativos para dispositivos m&#xF3;viles agn&#xF3;stio al sistema operativo.</mark></strong></blockquote><p>Entonces, se&#xF1;or reclutador, la pr&#xF3;xima vez que te acerques diciendo &quot;tengo una oferta incre&#xED;ble para un desarrollador frontend como t&#xFA; que sepa React para crear p&#xE1;ginas web&quot; no me env&#xED;as una &#xA1;m@ldit@ &quot;prueba&quot;! para &quot;desarrollar&quot; o contestar preguntas acerca de React Native.</p><p>&#xA1;No!, por favor no lo hagas, no lo intentes, no lo fuerces con palabras tipo &quot;pero es casi lo mismo, &#xBF;no?, entonces podr&#xED;as intentarlo, &#xBF;no?&quot; o llenes un p#t0 formulario indicando &quot;el desarrollador minti&#xF3; acerca de su experiencia&quot; porque ambos sabemos que no es as&#xED;, es un error tuyo como &quot;reclutador tech&quot; al no saber las diferencias. </p>]]></content:encoded></item><item><title><![CDATA[Buenas y ¡MALAS! experiencias con Tech Tests]]></title><description><![CDATA[<p>Tengo un trabajo estable (por los momentos, uno nunca sabe) en una compa&#xF1;&#xED;a que nos ha tratado bastante bien, pero nunca est&#xE1; de m&#xE1;s mirar otras oportunidades. Incluso, estoy donde estoy justamente por eso, por mirar qu&#xE9; hay m&#xE1;s all&#xE1;</p>]]></description><link>https://jorgetech.dev/buenas-y-malas-experiencias-con-tech-tests/</link><guid isPermaLink="false">681697509fea8903fd07e9aa</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Thu, 08 May 2025 20:26:31 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2025/05/001-feature.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2025/05/001-feature.png" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests"><p>Tengo un trabajo estable (por los momentos, uno nunca sabe) en una compa&#xF1;&#xED;a que nos ha tratado bastante bien, pero nunca est&#xE1; de m&#xE1;s mirar otras oportunidades. Incluso, estoy donde estoy justamente por eso, por mirar qu&#xE9; hay m&#xE1;s all&#xE1; del horizonte. Y si, s&#xE9; que lo &#xFA;ltimo son&#xF3; muy sentimental, pero el hecho es que si hubiera esperado que alguna oportunidad o alg&#xFA;n evento se moviera s&#xF3;lo o sucediera espont&#xE1;neamente donde estaba antes, tal vez nunca hubiera tomado o visto si quiera esta oportunidad donde he aprendido m&#xE1;s de lo imaginado y me han dejado experimentar y probar nuevas tecnolog&#xED;as, siempre en busca innovar.</p><p>Ahora, como les ven&#xED;a contando, he visto unas ofertas bastantes buenas, donde por primera vez siento que cumplo los requisitos:</p><ul><li>Experiencia mayor a 5 a&#xF1;os en alguna tecnolog&#xED;a.</li><li>Haber trabajado o conocer de servicios de c&#xF3;mputo en la nube.</li><li>Soft-skills que he madurado completamente.</li></ul><p>Antes, cuando me presentaba, no era nunca la primera opci&#xF3;n porque no cumpl&#xED;a con la experiencia, pero trataba de compensarlo con un r&#xE1;pido aprendizaje y demostrar unas ganas incre&#xED;bles de sacrificarme para cumplir con sus objetivos, siempre estudiando mucho m&#xE1;s all&#xE1; de lo que ped&#xED;an en los requisitos.</p><p>Pero ahora que la experiencia me ha quemada bastantes pesta&#xF1;as, he aprovechado para presentarme a uno que otra postulaci&#xF3;n, y como este es mi blog, quiero expresar mis buenas y malas (<strong>&#xA1;s&#xFA;per frustrantes!</strong>) experiencias. Ok, ser&#xE9; sincero, les hablar&#xE9; de 1 experiencia buena que tuve y otra mala (muy mala). No se si esto es deshago, o tal vez quiero dejar saber a los dem&#xE1;s o a mi futuro yo de c&#xF3;mo prepararse para afrontar estas situaciones.</p><p><strong>TLDR</strong>; Quiero redactar acerca de mis experiencias en entrevistas y pruebas <em>tech </em>para oportunidades de trabajo en las que me he presentado, de manera que pueda dejar por ac&#xE1; algo de la frustraci&#xF3;n (todos sabemos lo negativo que es el rechazo, no importa sobre qu&#xE9; tema sea) y si es posible, <mark>que esas empresas puedan ver el impacto que generan, lo mal que nos hacen sentir, los errores que est&#xE1;n cometiendo para seleccionar personal y puedan cambiar su proceso en lo posible</mark>, pero tambi&#xE9;n dejarles saber como otras compa&#xF1;&#xED;as si parecen entender que tratan con seres humanos y tienen muy bien planteado qu&#xE9; hacer y retos poner para sacar lo mejor de uno durante las entrevistas y pruebas.</p><p>NOTA: Ahora, s&#xE9; que no soy experto en selecci&#xF3;n y no tengo la teor&#xED;a o el <em>know-how</em>, pero qu&#xE9;date conmigo en esta lectura y dime si de verdad estoy interpretando mal lo sucedido o de verdad estas empresas no tienen ni la menor idea de las pruebas de selecci&#xF3;n que est&#xE1;n poniendo a sus vacantes.</p><h2 id="buena-experiencia-sent%C3%AD-que-di-lo-mejor-de-mi-y-m%C3%A1s-que-una-entrevista-me-dieron-la-oportunidad-de-lucir-mis-conocimientos">Buena experiencia: Sent&#xED; que di lo mejor de mi y m&#xE1;s que una entrevista, me dieron la oportunidad de lucir mis conocimientos</h2><p>Mi &#xFA;ltima experiencia, o mejor dicho, el &#xFA;ltimo proceso de selecci&#xF3;n que present&#xE9; fue muy bueno y me sent&#xED; c&#xF3;modo. De todas maneras tambi&#xE9;n tengo sentimientos negativos porque, s<em>poiler alert</em>: no qued&#xE9; seleccionado; ese rechazo me hizo sentir mal, pero igual les contar&#xE9; que siento que fue bueno para mi salud mental.</p><p><strong>NOTA: Si quieres leer mi mala experiencia y saber qui&#xE9;n fue que me hizo sentir mal y no tengo problemas en decir su nombre ac&#xE1;, podemos adelantarnos o <em>scrollear </em>hasta la secci&#xF3;n &apos;El &quot;proyecto demo&quot; m&#xE1;s est&#xFA;pido...&apos;, la segunda parte de este post.</strong></p><h3 id="%C2%BFc%C3%B3mo-fue-el-proceso-y-qu%C3%A9-tanto-me-preguntaron">&#xBF;C&#xF3;mo fue el proceso y qu&#xE9; tanto me preguntaron?</h3><p>Todo empez&#xF3; con una oferta en LinkedIn (no se en qu&#xE9; otro sitio uno consigue ofertas de trabajo hoy en d&#xED;a) y envi&#xE9; el correo con mi hoja de vida. No pas&#xF3; mucho tiempo para indicarme que pr&#xF3;ximamente, otra persona que no es la del correo, se iba a comunicar conmigo, por lo que supongo que una persona mira y &quot;aprueba&quot; hojas de vida y otra hace la primera entrevista.</p><p>Al cabo de un par de d&#xED;as me contactan, me preguntan si tengo un espacio para hablar de 30 mins, y llega el momento: una entrevista simple de mis estudios y mis experiencias, nada muy profundo y s&#xF3;lo una corrobaci&#xF3;n de datos. Incluso me preguntaron sobre mis experiencias como Ingeniero Mec&#xE1;nico, lo cu&#xE1;l es inusual en este campo de la inform&#xE1;tica. No faltaron las 2 preguntas en ingl&#xE9;s para saber si dec&#xED;a mentiras o la verdad acerca de mi nivel, por lo que respond&#xED; todo y me sent&#xED; bien, aunque mi esposa dice que debo mejorar mi pronunciaci&#xF3;n.</p><p>Al final de la llamada me indican que recibir&#xE9; un correo con una peque&#xF1;a prueba a realizar. Esto de pruebas en LeetCode o TestGorilla es bastante molesto. <strong>Muchas empresas se han mudado a esta modalidad y dejan toda la responsabilidad en ellas</strong>, lo cual siento que no es una fuente confiable de capacidades de la persona a evaluar, ya que solo reflejan si la persona sabe alg&#xFA;n m&#xE9;todo eficiente para hacer alguna l&#xF3;gica o algoritmo, y no el c&#xF3;mo afrontar y resolver problemas. Pero lo que me impact&#xF3; es que esta prueba fue super corta, 2 preguntas y 1 desarrollo de c&#xF3;digo que literal era s&#xFA;per sencillo.</p><p>Entonces le vi sentido a la prueba: no es para dejar todo en su responsabilidad y de aqu&#xED; seleccionar al mejor programador (al que se memoriz&#xF3; todo de YouTube), sino descartar esos que de verdad se est&#xE1;n presentando s&#xF3;lo por presentarse.</p><p>Finalic&#xE9; la prueba y al cabo de 1 d&#xED;a lleg&#xF3; un correo para una segunda entrevista, que va a tener un tono m&#xE1;s t&#xE9;cnico, y que adicional los ayude con unos datos m&#xED;os. El correo lleg&#xF3; con otra sorpresa, no s&#xF3;lo me pidieron mi experiencia y mi rol, <mark>sino que nombrara todos los proyectos en los que he trabajado, junto con su <em>Tech Stack</em> y asignaci&#xF3;n de equipo</mark>. Un poco laboriosa esta tarea ya que era recordar en detalle todo lo que hab&#xED;a hecho.</p><p>Avis&#xE9; que iba a tardar m&#xE1;s de lo esperado y para mi sorpresa, respondieron en un tiempo considerable (creo que en 2 horas) indicando que no hab&#xED;a problemas, que ten&#xED;a chance hasta un par de d&#xED;as antes de la otra entrevista y que agradec&#xED;a la comunicaci&#xF3;n (cre&#xE1;nme, yo tambi&#xE9;n).</p><p>Lleg&#xF3; el d&#xED;a de la entrevista, y m&#xE1;s o menos lo que imaginaba: me entrevistaba alguien con bastante experiencia en programaci&#xF3;n y me hac&#xED;a preguntas pre-formuladas para saber si conoc&#xED;a o no. Bien, ac&#xE1; empieza todo a decaer. Soy un programador auto-aprendido o <em>self-thought programmer</em>, <strong>lo que significa que aprend&#xED; por mis propios medios y no a trav&#xE9;s de m&#xE9;todos tradicionales. Esto traduce que conceptos b&#xE1;sicos o definiciones te&#xF3;ricas, normalmente me las salto o las desconozco porque simplemente s&#xE9; como funcionan en la pr&#xE1;ctica y s&#xE9; que funcionan as&#xED; y punto</strong>. No me fue del todo mal, pero les dejar&#xE9; el resumen (que era para una entrevista para un rol en Node.js):</p><ul><li>&quot;&#xBF;Sabes qu&#xE9; es el Event Loop y lo puedes definir?&quot; &#x2192; Esto lo respond&#xED; bien, pero necesito aprenderlo o saber definirlo en mejor detalle.</li><li>&quot;&#xBF;Sabes qu&#xE9; son bases de datos SQL y NoSQL?&quot; &#x2192; Tambi&#xE9;n se bastante de esto y creo que me luc&#xED; ac&#xE1;.</li><li>&quot;&#xBF;C&#xF3;mo funcionan las promesas en Node.js?&quot; &#x2192; Tambi&#xE9;n sin mucho inconveniente, pero me falta mejor definici&#xF3;n t&#xE9;cnica.</li><li>&quot;&#xBF;C&#xF3;mo funciona el async/await en Node.js?&quot; &#x2192; Parecido a las promesas, pero obviamente resalt&#xE9; las diferencias.</li><li>&quot;&#xBF;Qu&#xE9; es un REST API?, &#xBF;Qu&#xE9; significa cada palabra o acr&#xF3;nimo?, &#xBF;C&#xF3;mo est&#xE1; compuesto una llamada de este tipo?&quot; &#x2192; Ac&#xE1; empec&#xE9; a salir mal. S&#xE9; c&#xF3;mo funciona y c&#xF3;mo se usa, pero no puedo responder bien c&#xF3;mo se define t&#xE9;cnicamente.</li><li>&quot;&#xBF;Puedes nombrarme los Patrones de Dise&#xF1;o o <em>Design Patters</em> de JavaScript? &#x2192; Primera respuesta totalmente err&#xF3;nea o que no sab&#xED;a, de verdad creo que esto me lo he saltado en mi aprendizaje.</li><li>&quot;&#xBF;Puedes explicarme la diferencia entre programaci&#xF3;n funcional y programaci&#xF3;n orientada a objetos?&quot; &#x2192; Otras preguntas donde no ten&#xED;a la respuesta, simplemente sal&#xED; mal.</li></ul><p>Hab&#xED;an muchas otras preguntas, pero vamos a dejar esta lista hasta ac&#xE1;. Uds. creer&#xED;an que iba a salir bien tomando en cuenta que el grueso de las preguntas y otras de servicios en la nube me iban a dejar bien parado, y m&#xE1;s a&#xFA;n tomando en cuenta que el <em>feedback </em>del entrevistador es bueno.</p><h3 id="%C2%BFy-el-resultado-final-%C2%BFme-seleccionaron">&#xBF;Y el resultado final?, &#xBF;Me seleccionaron?</h3><p>Lamentablemente, al d&#xED;a siguiente, recib&#xED; el correo con que mi postulaci&#xF3;n llegaba hasta ese punto y agradec&#xED;an mi tiempo y compromiso. <strong>Se que lo estoy poniendo ac&#xE1; en simples palabras, pero este proceso es uno de los que m&#xE1;s fuerte me ha pegado a mi autoestima, a mi ego y mi ser; de verdad lo recib&#xED; mal. Y pas&#xE9; todo el d&#xED;a deprimido, <mark>perd&#xF3;n esposa, perd&#xF3;n compa&#xF1;eros</mark></strong>.</p><p>Decid&#xED; preguntar, uno nunca sabe, y de nuevo, en menos de 2 horas, el chico encargado del proceso de selecci&#xF3;n, el de la entrevista sencilla, me dej&#xF3; saber que en realidad sal&#xED; bien, que s&#xF3;lo me qued&#xF3; por mejorar &quot;JS Design Patterns&quot; y &quot;Functional Programming&quot;, pero decidieron ir por otros postulantes con mejor resultado que yo para la siguiente fase.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2025/05/image-1.png" class="kg-image" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" loading="lazy" width="1535" height="518" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image-1.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image-1.png 1000w, https://jorgetech.dev/content/images/2025/05/image-1.png 1535w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Leyendo esto de nuevo, creo que es un buen </span><i><em class="italic" style="white-space: pre-wrap;">feedback</em></i><span style="white-space: pre-wrap;">, no me hace sentir &quot;tan mal&quot;.</span></figcaption></figure><h3 id="%C2%BFpor-qu%C3%A9-creo-que-fue-buena-la-experiencia">&#xBF;Por qu&#xE9; creo que fue buena la experiencia?</h3><p>Creo que fue bueno porque en ning&#xFA;n momento me hicieron sentir mal por no saber alg&#xFA;n tema o no cumplir, simplemente me dijeron que hab&#xED;an mejores postulantes, y creo que es una respuesta formal y bastante amigable (no s&#xE9; que tan cierto sea, pero la sent&#xED; sincera).</p><p>Pero sobre todo siento que fue buena porque no fue una prueba tonta o el despliegue de un &quot;proyecto demo&quot; (&#xA1;Uy! ya vamos a hablar de este tipo de pruebas en la mala experiencia) para demostrar mis habilidades, fueron unas pruebas y preguntas t&#xE9;cnicas para descartar y filtrar de forma correcta, incluso si no sab&#xED;a la definici&#xF3;n t&#xE9;cnica, el evaluador me dice que lo pod&#xED;a decir con mis propias palabras y la val&#xED;a, s&#xF3;lo que de verdad esos temas que dije se me van mucho de las manos (al menos el origen y el porqu&#xE9; se usan).</p><p>Tambi&#xE9;n siento que fue buena <mark>porque me deja un <em>roadmap </em>sincero para seguir mejorando e intentando</mark>, cosa que veremos que los dem&#xE1;s no dejan claro. Ya s&#xE9; cu&#xE1;les fueron mis puntos d&#xE9;biles y s&#xE9; que voy a hacer para mejorar, pero continuemos con nuestro siguiente tema, qu&#xE9; es donde quiero dejar toda mi frustraci&#xF3;n.</p><h2 id="el-proyecto-demo-m%C3%A1s-est%C3%BApido-que-he-hecho-la-evaluaci%C3%B3n-m%C3%A1s-sin-sentido-sin-direcci%C3%B3n-sin-explicaci%C3%B3n-sin-respuesta-a-dudas-oportuna-y-el-feedback-m%C3%A1s-mentiroso-que-haya-visto">El &quot;proyecto demo&quot; m&#xE1;s est&#xFA;pido que he hecho, la evaluaci&#xF3;n m&#xE1;s sin sentido, sin direcci&#xF3;n, sin explicaci&#xF3;n, sin respuesta a dudas oportuna y el <em>feedback </em>m&#xE1;s mentiroso que haya visto</h2><p>Como les dije, ac&#xE1; es donde quiero descargarme, as&#xED; que empecemos. Nombre de la empresa: <a href="https://www.alegra.com/colombia/contabilidad/?ref=jorgetech.dev" rel="noreferrer">Alegra</a>, ofrecen un SaaS para solucionar temas de contabilidad a otras empresas (B2B).</p><h3 id="%C2%BFc%C3%B3mo-fue-el-proceso-%C2%BFpor-qu%C3%A9-no-lo-vi-venir-antes">&#xBF;C&#xF3;mo fue el proceso?, &#xBF;Por qu&#xE9; no lo vi venir antes?</h3><p>Esto sucedi&#xF3; antes que la buena experiencia, que para el momento de esta redacci&#xF3;n, es la &#xFA;ltima que entrevista que he realizado. Fue muy parecido, vi una oferta, vi que cumpl&#xED;a y me present&#xE9;. Al poco tiempo me escriben y creo que en este caso me env&#xED;an un enlace para agendar la pr&#xF3;xima llamada.</p><p>Tambi&#xE9;n que la otra, una entrevista de experiencias y conocimientos, pero muy por encima, ya que la persona entrevistando es de perfil administrativo, entonces no sabe mucho de tecnicismo, de c&#xF3;mo es esto de la tecnolog&#xED;a. De nuevo, un par de preguntas en ingl&#xE9;s y todo bien por ac&#xE1;. Se termina la llamada y me indica que me va a llegar un correo con una pruebita a realizar.</p><p>Ok, nada mal, una prueba en TestGorilla. Me env&#xED;an el enlace, la hago, y ac&#xE1; empiezan las cosas negativas, no tengo respuesta ni al siguiente d&#xED;a ni a la siguiente semana.<strong> </strong><mark>Le escribo un correo a la chica de selecci&#xF3;n que me hizo la entrevista y nada, sin respuesta por un mes completo</mark>.</p><p>Para este momento ya hab&#xED;a dejaron como terminada la prueba. Probablemente hab&#xED;a salido mal o ya hab&#xED;an seleccionado a la otra persona (sinceramente creo que no me hab&#xED;a ido mal). Pero al cabo de un mes me lleg&#xF3; un correo felicit&#xE1;ndome e indicando que pod&#xED;a continuar con la segunda fase que era hacer un reto t&#xE9;cnico.</p><p>No recuerdo si tuvimos una segunda llamada o no, pero esta vez le mencion&#xE9; a Andrea, la seleccionadora, que justo esta semana era mi mudanza y probablemente no iba a tener mucho tiempo el fin de semana para hacer la prueba, pero iba a hacer mi mayor esfuerzo. Esta vez si me respondi&#xF3; y me dijo que no hab&#xED;a problema, que avisara y con gusta me aplazaban el momento de la entrega.</p><p>Bueno, al menos me respondieron eso. Listo, hora de revisar la prueba: <strong>me ped&#xED;an hacer un peque&#xF1;o software para una situaci&#xF3;n ficticia donde quer&#xED;a que usara mis conocimientos en desarrollo de una soluci&#xF3;n en microservicios</strong>. Viendo la prueba por encima, uno podr&#xED;a pensar que no tiene nada malo, pero ac&#xE1; les cuento mi opini&#xF3;n de la misma luego de leerla a detalle:</p><ul><li>Algunos errores de redacci&#xF3;n que confunden el c&#xF3;mo hacer o definir ciertas l&#xF3;gicas.</li><li>No indican qu&#xE9; tipo Patrones de Dise&#xF1;o de Arquitectura (req/resp o pub/sub).</li><li>No piden pruebas unitarias o de integraci&#xF3;n (detalle m&#xE1;s adelante de porqu&#xE9; no se implement&#xF3;).</li><li>Piden desplegar el aplicativo por sus propios medios. La mayor estupidez del mundo, uno incurriendo a gastos porque ellos no son capaces de desplegar un servidor para esto, pero no importa, existen algunos servicios gratuitos (con sus grandes limitaciones) que podemos usar.</li><li>Piden que la soluci&#xF3;n soporte peticiones masivas, pero son tan est&#xFA;pidos para indicar que la dependencia del rendimiento es de la infraestructura en vez del c&#xF3;digo y capas. O sea, yo apliqu&#xE9; una cola de mensajes o <em>Queue </em>(aparte del <em>Message Broker</em> para la comunicaci&#xF3;n de servicios) para demostrar que conozco sobre estas implementaciones para estos problemas, <strong>pero ellos esperaban que pagara un servidor de s&#xFA;per alto rendimiento de m&#xFA;ltiples n&#xFA;cleos y mucha RAM</strong>. De nuevo, que requisito tan idiota en la vida.</li><li>El mayor requisito de desarrollo, el principal que decida por donde iniciar o qu&#xE9; framework elegir, <mark>literal es la &#xFA;ltima l&#xED;nea del documento dentro del t&#xED;tulo &quot;Consideraciones&quot; (t&#xED;tulo que indica que son apenas una observaci&#xF3;n adicional)</mark>.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2025/05/image.png" class="kg-image" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" loading="lazy" width="1139" height="669" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image.png 1000w, https://jorgetech.dev/content/images/2025/05/image.png 1139w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">El requisito principal del &quot;proyecto demo&quot; dejado como &#xFA;ltima l&#xED;nea en una secci&#xF3;n simplemente de observaciones o consideraciones.</span></figcaption></figure><p>De nuevo, una total estupidez de redacci&#xF3;n. <strong>&#xA1;Este &#xFA;ltimo texto debe ser la primera l&#xED;nea de toda la prueba!</strong></p><h3 id="por-culpa-de-ellos-debo-pedir-una-semana-adicional">Por culpa de ellos debo pedir una semana adicional</h3><p>Ok, decid&#xED; comunicarles este peque&#xF1;o detalle, ya que todo mi aplicativo estaba en Nest.js, y este framework no es considerado &quot;Nodejs puro&quot;. Por suerte, luego de un par de correos (s&#xED;, m&#xE1;s de uno porque no me respond&#xED;an) me dicen que si, que puedo tomarme un tiempo adicional para ello.</p><p>De inmediato empiezo a trabajar en la traducci&#xF3;n a Node.js puro con Express, pero tomen en cuenta que este no es un trabajo f&#xE1;cil y requiere un esfuerzo. De repente no el mismo esfuerzo de crearlo desde cero, pero es un desgaste adicional al primero que tuve, porque si pueden leer la prueba, piden un demo que tenga 3 microservicos (4 en total si tomamos en cuenta el API Gateway).</p><p>Tambi&#xE9;n es algo desmoralizante trabajar en algo que ya has resuelto, pero bueno, otra semana m&#xE1;s de acostarme hasta tarde (soy un adulto funcional con responsabilidades fuera de mi horario laboral), pero se logr&#xF3;, y a que no adivinan: <mark>Resulta y acontece que el texto de la prueba estaba incompleto y deciden enviarme un texto corregido con tareas adicionales... xD!</mark></p><h3 id="%C2%A1%C2%BFqu%C3%A9-o-sea-%C2%BFdebo-acomodar-mi-proyecto-ya-reacomodado-a-los-nuevos-requerimientos-de-la-prueba">&#xA1;&#xBF;Qu&#xE9;?! O sea, &#xBF;Debo acomodar mi proyecto ya reacomodado a los nuevos requerimientos de la prueba?</h3><p>Si, debo ajustar la prueba, y este error recae en uno de los primeros puntos mencionados: la prueba no tiene un definici&#xF3;n a detalle o no saben qu&#xE9; es lo que quieren ver o medir. Estos son errores humanos y es entendible, <strong>pero lo que no es entendible es que uno tenga que pagar en vez de ellos</strong>, eso est&#xE1; completamente mal, ellos son los que deber&#xED;an tomar la prueba con los requisitos como est&#xE1;n en vez de pedirme m&#xE1;s esfuerzo para ajustarme a ellos.</p><p>Pero no importa, al menos no es un cambio radical, pero si me piden cosas nuevas como a&#xF1;adir pruebas unitarias, que no fueron a&#xF1;adidas por tema de tiempos, esfuerzos y lo principal, en ninguna secci&#xF3;n mencionaron esto o que era parte de la evaluaci&#xF3;n, pero bueno, nada que no se pueda hacer.</p><p>Y casi una semana adicional m&#xE1;s de esfuerzos y listo, pude terminar la prueba. &#xBF;Saben qu&#xE9; es lo peor? Que luego de unos correos indicando que estaba usando servicios gratuitos (Render + Vercel + Supabase como excelentes servicios), casi de inmediato como si ya lo tuvieran redactado, <mark>me env&#xED;an un correo indicando que no pas&#xE9; esta fase y me dan unos puntos a mejorar sacados de ChatGPT</mark>.</p><p>Esto es lo que m&#xE1;s me caus&#xF3; odio, rabia y frustraci&#xF3;n. La decisi&#xF3;n ya estaba tomada, y la justificaci&#xF3;n era sacado de un texto de ChatGPT donde me indicaron que &quot;debo mejorar&quot; en aspectos que ni siquiera pidieron. Por ejemplo, me piden que mejore en &quot;DevOps y Automatizaci&#xF3;n&quot; a&#xF1;adiendo <em>CI/CD Pipelines</em> y que usara tecnolog&#xED;as como Terraform. <strong>Si uds. quer&#xED;an evaluarme en Terraform, era solo cuesti&#xF3;n de pedirlo (ni en el <em>Job Description</em> ni en la prueba estaba este requerimiento).</strong></p><p>Pero no se preocupen, ac&#xE1; les dejo el texto que me dejaron y mi respuesta (claro que iba a responder, estaba furioso).</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192619235.png" width="1546" height="839" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_192619235.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_192619235.png 1000w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192619235.png 1546w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192647684.png" width="1540" height="797" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_192647684.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_192647684.png 1000w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192647684.png 1540w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192722503.png" width="1536" height="799" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_192722503.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_192722503.png 1000w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192722503.png 1536w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192805088.png" width="1556" height="824" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_192805088.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_192805088.png 1000w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192805088.png 1556w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192827330.png" width="1525" height="641" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_192827330.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_192827330.png 1000w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_192827330.png 1525w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Este es el &quot;feedback&quot; que recib&#xED;, completamente salido de un LLM.</span></p></figcaption></figure><p>Como ven, no pod&#xED;a quedarme callado, tard&#xE9; 1 hora en escribir todo el texto y en responderles, porque de verdad es un correo bastante ofensivo.</p><h3 id="entonces-%C2%BFsi-fue-una-mala-experiencia-con-alegra">Entonces, &#xBF;Si fue una mala experiencia con Alegra?</h3><p>&#xA1;Claro que si! De verdad no se si es parte de la empresa, del &#xE1;rea de selecci&#xF3;n, de los evaluadores, o de la reclutadora en espec&#xED;fico, pero siento que algo est&#xE1; mal o algo fallo. Me gustar&#xED;a pensar que as&#xED; como tom&#xE9; estos eventos para fortalecerme en conocimientos (y como obtener paz mental), espero que si alguien de por all&#xE1; lee esto, tambi&#xE9;n puedan fortalecer su proceso.</p><h2 id="desenlace-qu%C3%A9-me-llevo-de-estas-experiencias-y-qu%C3%A9-les-puedo-dejar">Desenlace: qu&#xE9; me llevo de estas experiencias y qu&#xE9; les puedo dejar</h2><p>En t&#xE9;rminos generales, me llevo el conocimiento de c&#xF3;mo pueden ser las pr&#xF3;ximas entrevistas, qu&#xE9; me pueden preguntar y qu&#xE9; conceptos debo mejorar. Por ejemplo, mi &#xFA;ltima buena experiencia de verdad me deja motivado a entender las bases de programaci&#xF3;n web, al menos las de Node.js y JavaScript/TypeScript.</p><p>Y les puedo dejar esta experiencia para que, si en alg&#xFA;n momento les sucede una parecida (ya sea buena o mala), de pronto puedan tomar la misma iniciativa de escoger los puntos a mejorar para una pr&#xF3;xima vez (y tal vez escribir un post acerca de su frustraci&#xF3;n hacia alguna en espec&#xED;fico, no se).</p><p>Si quieren conocer las pruebas que dejaron, practicarlas para su futuro, gu&#xED;arse en qu&#xE9; podr&#xED;an hacer o qu&#xE9; no deben hacer (o incluso, para presentarse a esta misma), se las dejo por ac&#xE1; en la siguiente lista de enlaces.</p><ul><li>Primer documento con requisitos de &quot;proyecto demo&quot;, Tech Test o reto t&#xE9;cnico de Alegra: <a href="https://docs.google.com/document/d/1SiyunJ5Pw-jVdEdbDCzcVBrFSuNgGn47xwkl72glBxo/edit?usp=sharing&amp;ref=jorgetech.dev" rel="noreferrer">Reto t&#xE9;cnico Node - 2024 - candidate</a></li><li>Segundo documento con requisitos de &quot;proyecto demo&quot;, Tech Test o reto t&#xE9;cnico de Alegra: <a href="https://alegraweb.notion.site/Jornada-de-Almuerzo-Gratis-1b355f93676580f08a86fed4bf0ea30c?ref=jorgetech.dev" rel="noreferrer">Jornada de Almuerzo Gratis - Desaf&#xED;o Backend Node.js</a></li><li>Repositorio en GitHub con c&#xF3;digo fuente de primera versi&#xF3;n de mi reto t&#xE9;cnico en Nest.js: <a href="https://github.com/jcontrerasprince/alegra-kitchen-test?ref=jorgetech.dev" rel="noreferrer">jcontrerasprince/alegra-kitchen-test</a></li><li>Repositorio en GitHub con c&#xF3;digo fuente de segunda versi&#xF3;n de mi reto t&#xE9;cnico en &quot;Node.js puro&quot;: <a href="https://github.com/jcontrerasprince/alegra-kitchen-test-v2?ref=jorgetech.dev" rel="noreferrer">jcontrerasprince/alegra-kitchen-test-v2</a></li><li>Enlace p&#xFA;blico para probar aplicativo: <a href="https://alegra-kitchen-test-v2.vercel.app/?ref=jorgetech.dev" rel="noreferrer">Alegra | Kitchen test</a></li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_195130825.png" width="1807" height="953" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_195130825.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_195130825.png 1000w, https://jorgetech.dev/content/images/size/w1600/2025/05/image_2025-05-05_195130825.png 1600w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_195130825.png 1807w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2025/05/image_2025-05-05_195212421.png" width="1772" height="954" loading="lazy" alt="Buenas y &#xA1;MALAS! experiencias con Tech Tests" srcset="https://jorgetech.dev/content/images/size/w600/2025/05/image_2025-05-05_195212421.png 600w, https://jorgetech.dev/content/images/size/w1000/2025/05/image_2025-05-05_195212421.png 1000w, https://jorgetech.dev/content/images/size/w1600/2025/05/image_2025-05-05_195212421.png 1600w, https://jorgetech.dev/content/images/2025/05/image_2025-05-05_195212421.png 1772w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Captura de pantallas de c&#xF3;mo se ve el repositorio para invitarlos a revisar y opinar.</span></p></figcaption></figure><p>Tambi&#xE9;n, no es tema m&#xED;o c&#xF3;mo est&#xE9;n protegiendo sus procesos, pero si s&#xED; desean que un futuro sean m&#xE1;s segura y no la puedan compartir f&#xE1;cilmente, les voy a dejar una sugerencia de c&#xF3;mo podr&#xED;an hacerlo de mis propias palabras. Es m&#xE1;s, se las regalo (me pueden brindar un caf&#xE9; luego):</p><blockquote class="kg-blockquote-alt">Como empresa conformada, con riesgos asumidos y disposici&#xF3;n de recursos para obtenci&#xF3;n de resultados, pueden habilitar un repositorio privado desde su cuenta en GitHub (o GitLab, BitBucket, Azure DevOps, o lo que sea) y conectar un CI/CD Pipeline directamente a un servidor en su proveedor de c&#xF3;mputo en la nube (AWS, Azure, Digital Ocean, GCP (?)). De esta manera se aseguran que el repositorio no pueda quedar publicado, que los participantes no tengan que gastar en sus propios recursos, y ajustar el escalamiento vertical (m&#xE1;s CPU o m&#xE1;s RAM) a c&#xF3;mo uds. lo deseen.</blockquote><p>De esta manera uds. controlan el ambiente y la informaci&#xF3;n que se manipular&#xE1; (repositorio, requisitos, etc).</p><p>Pero en fin, no quiero agobiarlos m&#xE1;s. Despu&#xE9;s de escribir mi experiencia me siento un poco mejor y sigo teniendo mi motivaci&#xF3;n para continuar mi preparaci&#xF3;n continua, ya sea que quiera seguir donde estoy o quiero buscar campos m&#xE1;s verdes. De todas maneras si quieren que hablemos de alg&#xFA;n tema distinto o tiene alguna recomendaci&#xF3;n, s&#xF3;lo d&#xE9;jenme saber en un comentario o en mi perfil de <a href="https://www.linkedin.com/in/jcontrerasprince/?ref=jorgetech.dev" rel="noreferrer">LinkedIn</a>, y con mucho gusto lo discutimos. <strong>&#xA1;Que tengan muy buena semana!</strong></p>]]></content:encoded></item><item><title><![CDATA[Cómo versionar tu base de datos con "migraciones" en node.js y python]]></title><description><![CDATA[<p>TLDR; As&#xED; como tu aplicativo o soluci&#xF3;n tiene versiones, la base de datos tambi&#xE9;n debe tener versiones, las cuales pueden actualizarse a una versi&#xF3;n mayor a trav&#xE9;s de &quot;upgrades&quot; o ser revertidas a versiones anteriores con &quot;downgrades&quot;</p>]]></description><link>https://jorgetech.dev/versiones-o-migraciones-de-una-base-de-datos/</link><guid isPermaLink="false">66549f856bfb5c1355dcaece</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Wed, 11 Sep 2024 00:25:49 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/09/feature.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/09/feature.png" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python"><p>TLDR; As&#xED; como tu aplicativo o soluci&#xF3;n tiene versiones, la base de datos tambi&#xE9;n debe tener versiones, las cuales pueden actualizarse a una versi&#xF3;n mayor a trav&#xE9;s de &quot;upgrades&quot; o ser revertidas a versiones anteriores con &quot;downgrades&quot;. Esto asegura que tu aplicativo pueda ser usado en diferentes etapas de su evoluci&#xF3;n de acuerdo a los requerimientos con integraciones o actualizaciones del cliente. En este post hablamos sobre qu&#xE9; son &quot;migraciones&quot; de una base de datos y construimos 3 ejemplos para aplicativos en Node.js con Sequelize, Node.js con TypeORM y Flask con SQLAlchemy y Alembic.</p><p>Enlaces a documentaciones oficiales:</p><ul><li><a href="https://sequelize.org/master/manual/migrations.html?ref=jorgetech.dev">Documentaci&#xF3;n de Sequelize</a></li><li><a href="https://typeorm.io/?ref=jorgetech.dev#/">Documentaci&#xF3;n de TypeORM</a></li><li><a href="https://alembic.sqlalchemy.org/en/latest/?ref=jorgetech.dev">Documentaci&#xF3;n de Alembic</a></li><li><a href="https://flask-migrate.readthedocs.io/en/latest/?ref=jorgetech.dev">Documentaci&#xF3;n de Flask-Migrate</a></li></ul><p>Tambi&#xE9;n te dejo este enlace donde se encuentran los ejemplos de es Post en GitHub: <a href="https://github.com/jcontrerasprince/migraciones-nodejs-python?ref=jorgetech.dev">Migraciones con Node.js y Python</a>.</p><h2 id="entonces-%C2%BFqu%C3%A9-son-las-migraciones-en-una-base-de-datos">Entonces, &#xBF;Qu&#xE9; son las &quot;migraciones&quot; en una base de datos?</h2><p>De manera muy corta y sencilla, las migraciones en una base de datos es el versionamiento del esquema, o de las tablas o de la relaci&#xF3;n entre entidades (ERD -Entity Relationship Diagram-), de acuerdo con la evoluci&#xF3;n de tu aplicativo.</p><h2 id="ejemplo-de-un-aplicativo-web-de-correspondencia">Ejemplo de un aplicativo web de correspondencia</h2><p>En un ejemplo pr&#xE1;ctico: imagina que has desarrollado un aplicativo web para el seguimiento de env&#xED;o de correspondencia f&#xED;sica, como sobres y paquetes, que consta de un <em>frontend</em>, un <em>backend </em>y una base de datos.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=1785&amp;x=-166&amp;y=-220&amp;w=1432&amp;h=851&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%209631914b6c69bcb6d88cfb47d7502e27a60e1cbc62e19e93d0c1364d507bed77-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="1432" height="851"><figcaption><span style="white-space: pre-wrap;">Diagrama que muestra el flujo de interacciones entre distintas capas de un sistema de software. En este caso el usuario interact&#xFA;a con la capa de cliente y la informaci&#xF3;n viaja hasta la base de datos.</span></figcaption></figure><p>Este aplicativo tiene varias funciones y caracter&#xED;sticas, sin embargo, vamos a enfocarnos que un usuario final tiene la posibilidad de ver el pr&#xF3;ximo env&#xED;o a recibir en una interfaz que le muestra desde donde viene, a donde se va a entregar y qu&#xE9; d&#xED;a ser&#xE1; entregando, incluyendo una aproximaci&#xF3;n en la parte del d&#xED;a (ma&#xF1;ana, tarde o noche).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=1790&amp;x=1249&amp;y=-360&amp;w=2426&amp;h=1100&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%20466d9f755172fd4e4a15a2e1603198a7069f50721a1ce0af1227a9e969b75eb5-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="2426" height="1100"><figcaption><span style="white-space: pre-wrap;">Visualizaci&#xF3;n de informaci&#xF3;n de aplicativo ejemplo junto con los campos de informaci&#xF3;n disponibles en la versi&#xF3;n 1.0.</span></figcaption></figure><p>El aplicativo es perfecto, cumple con todas las solicitudes de los <em>stake-holders</em> o interesados y tus jefes est&#xE1;n sumamente contentos con el trabajo realizado.</p><p>Los usuarios finales deciden invertir para a&#xF1;adirle una segunda serie de funcionalidades y caracter&#xED;sticas a tu aplicativo, entre las cuales, est&#xE1; la posibilidad de indicar el peso total del paquete pr&#xF3;ximo a recibir porque los clientes quieren saber si es algo muy grande que deben manejar en la entrega.</p><p>Bien, el cambio no es dif&#xED;cil, es solo a&#xF1;adir una columna nueva en la base de datos, actualizar el esquema o clase en la l&#xF3;gica del <em>backend </em>y habilitar un campo en la interfaz del <em>frontend </em>para introducir esta informaci&#xF3;n.</p><p>Tu plan para llevar a cabo esta actualizaci&#xF3;n, y la de las dem&#xE1;s funcionalidades y caracter&#xED;sticas, es actualizar los repositorios, por lo cual ya tus aplicativos <em>frontend </em>y <em>backend </em>tienen la nueva l&#xF3;gica, y para tu base de datos, decides que entrar&#xE1;s al servidor de producci&#xF3;n y har&#xE1;s este cambio manualmente con un <code>ALTER TABLE pedidos ADD peso_kg DECIMAL (16, 4)</code>.</p><p>Como podr&#xE1;s notar, esto de entrar a los servidores en producci&#xF3;n es un &#xA1;no no no! Existen muchos riesgos y peligros, cr&#xE9;eme. Por m&#xE1;s experimentado que uno sea, siempre va a ocurrir el error al momento de alterar o modificar algo en una base de datos en producci&#xF3;n (por desgracia, aprend&#xED; esto a las malas).</p><h2 id="la-migraci%C3%B3n-de-la-base-de-datos-en-tu-aplicativo-de-correspondencia">La &quot;migraci&#xF3;n&quot; de la base de datos en tu aplicativo de correspondencia</h2><p>Para resolver este inconveniente de actualizaci&#xF3;n del esquema de la base de datos, creamos o &quot;generamos&quot; migraciones que se encarguen de alterar, a&#xF1;adir o eliminar objetos de tu base de datos de acuerdo al requerimiento de las nuevas versiones.</p><p>Pero recuerda esto super importante: as&#xED; como en tu control de versiones en GitHub, GitLab, BitBucket, Azure Devops, o el servicio que est&#xE9;s usando, debe existir tanto una forma de subir o mejorar tu versi&#xF3;n de base de datos (&quot;upgrade&quot;), como una forma de bajar o degradar la versi&#xF3;n (&quot;downgrade&quot;).</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1.png" width="1763" height="827" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1.png 1600w, https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1.png 1763w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1--1-.png" width="2000" height="886" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1--1-.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1--1-.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1--1-.png 1600w, https://jorgetech.dev/content/images/size/w2400/2024/06/Migraciones---Page-1--1-.png 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/06/Migraciones---Page-1--2-.png" width="2000" height="855" loading="lazy" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" srcset="https://jorgetech.dev/content/images/size/w600/2024/06/Migraciones---Page-1--2-.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/06/Migraciones---Page-1--2-.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/06/Migraciones---Page-1--2-.png 1600w, https://jorgetech.dev/content/images/size/w2400/2024/06/Migraciones---Page-1--2-.png 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Diagrama que muestra la evoluci&#xF3;n de la base de datos de acuerdo a los requerimientos. Tambi&#xE9;n se observa una visualizaci&#xF3;n de la aplicaci&#xF3;n de ejemplo en su versi&#xF3;n 2.0.</span></p></figcaption></figure><p>Entonces vamos a crear 2 m&#xE9;todos o funciones que hagan exactamente esto mismo:</p><ol><li>La funci&#xF3;n de <code>upgrade</code> se encarga mejorar la base de datos desde la versi&#xF3;n v1.0 a la v2.0.</li><li>La funci&#xF3;n de <code>downgrade</code> se encarga de degradar la base de datos desde la versi&#xF3;n v2.0 a la v.10.</li></ol><p>Puede suceder el caso de que un error inesperado afecta producci&#xF3;n y la mejor soluci&#xF3;n es revertir todos los cambios y retornar a una versi&#xF3;n anterior. En nuestro ejemplo ser&#xED;a regresar a la versi&#xF3;n v1.0, por lo tanto es importante tener la funci&#xF3;n <code>downgrade</code> disponible.</p><p>As&#xED; como hacemos <code>git revert</code> para revertir cambios en el repositorio y retornarlo a un estado anterior, lo mismo logramos con el m&#xE9;todo <code>downgrade</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://documents.lucid.app/documents/19bd4a55-d1ba-4722-be05-2af6b89d527d/pages/0_0?a=2477&amp;x=6677&amp;y=-197&amp;w=926&amp;h=794&amp;store=1&amp;accept=image%2F*&amp;auth=LCA%2085efee722b60ee7537204a73b9fb3cfea9137d32c9fc969687a42ec68dafdc12-ts%3D1719176883" class="kg-image" alt="C&#xF3;mo versionar tu base de datos con &quot;migraciones&quot; en node.js y python" loading="lazy" width="926" height="794"><figcaption><span style="white-space: pre-wrap;">Representaci&#xF3;n de c&#xF3;mo se hace un cambio de versi&#xF3;n usando el c&#xF3;digo fuente y un aplicativo para el manejo de versionamiento y el parecido con las migraciones de bases de datos.</span></figcaption></figure><p>Listo, de esta forma logras varios objetivos en tu aplicativo:</p><ul><li>Manejar tu base de datos con un control de versiones.</li><li>Hacer actualizaciones de forma programada con cada salida o liberaci&#xF3;n de nueva versi&#xF3;n de tu aplicativo.</li><li>Tener un camino o v&#xED;a para retornar los cambios en cualquier punto de tu aplicativo.</li></ul><p>Si, a pesar de que es mayor trabajo al momento de desarrollar tu aplicativo, es la mejor forma de tener un control y trazabilidad completa de tu esquema de base de datos.</p><h2 id="ahora-%C2%BFhay-algunos-ejemplos-pr%C3%A1cticos-que-podamos-usar-en-nodejs-o-python">Ahora, &#xBF;Hay algunos ejemplos pr&#xE1;cticos que podamos usar en Node.js o Python?</h2><p>Si, para esta pregunta te voy a presentar 2 ejemplos en Node.js, uno usando la librer&#xED;a Sequelize y otro usando la librer&#xED;a TypeORM. El &#xFA;ltimo ejemplo lo haremos en Python con la librer&#xED;a Alembic.</p><p>De nuevo, todo el c&#xF3;digo referente a estos ejemplos lo encuentras en <a href="https://github.com/jcontrerasprince/migraciones-nodejs-python?ref=jorgetech.dev">Migraciones con Node.js y Python</a></p><h3 id="aplicativo-en-nodejs-javascript-usando-sequelize">Aplicativo en Node.js (JavaScript) usando Sequelize</h3><p>En este primer ejemplo creamos una aplicaci&#xF3;n desde cero en Node.js usando las librerias dotenv, express, pg, pg-hstore y sequelize. Como dependencias de desarrollador usamos las librerias de nodemon y sequelize-cli.</p><p>Como vamos a usar dotenv, creamos un archivo <code>.env</code> el cual tendr&#xE1; la variable <code>DATABASE_CONNECTION_URI = postgres://usuario:contrase&#xF1;a@localhost:5432/migrations_sequelize</code> apuntando a la base de datos.</p><p>Creamos el archivo <code>.sequelizerc</code> para indicar los paths hacia los distintos archivos de configuraci&#xF3;n. En este ejemplo indicamos donde se encuentra el archivo <code>config.js</code>.</p><p>En el archivo <code>config.js</code> indicamos el objeto &quot;development&quot; para nuestras variables en el ambiente de desarrollo e indicamos los valores de <code>url</code> y <code>dialect</code> para nuestra base de datos.</p><p>Creamos el directorio <code>migrations</code> e indicamos nuestras migraciones creando documentos con el formato <code>&lt;timestamp&gt;-&lt;nombre-migracion&gt;.js</code>. Y en este archivo definimos las funciones <code>up</code> y <code>down</code> a ser exportadas donde est&#xE1; la l&#xF3;gica de subida de versi&#xF3;n y bajada de versi&#xF3;n de la base de datos.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">&quot;use strict&quot;;

/** @type {import(&apos;sequelize-cli&apos;).Migration} */
module.exports = {
  up: (queryInterface, Sequelize) =&gt; {
    // l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n
  },
  down: (queryInterface, Sequelize) =&gt; {
    // l&#xF3;gica para revertir los cambios
  },
};</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>up</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>down</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en Sequelize.</span></p></figcaption></figure><p>Otra forma de crear este archivo es a trav&#xE9;s del uso de <code>sequelize-cli</code>, cuya herramienta nos permite usar el comando <code>npx sequelize-cli model:generate --name MyModel</code> el cual crear&#xE1; un archivo con el nombre <code>&lt;timestamp&gt;-create-user.js</code> en la carpeta <code>migrations</code>.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://sequelize.org/master/manual/migrations.html?ref=jorgetech.dev" rel="noreferrer"><em>Sequelize Migrations</em></a><em>.</em></p><h3 id="aplicativo-en-nodejs-typescript-usando-typeorm">Aplicativo en Node.js (TypeScript) usando TypeORM</h3><p>En este segundo ejemplo creamos una aplicaci&#xF3;n desde cero en Node.js usando las librerias dotenv, express, pg, reflect-metadata y typeorm. Como dependencias de desarrollador usamos las librerias de @types/express, @types/node, nodemon, ts-node y typescript.</p><p>Con el uso de dotenv vamos a crear un archivo <code>.env</code> con la variable <code>DATABASE_CONNECTION_URI = postgres://usuario:contrase&#xF1;a@localhost:5432/migrations_typeorm</code> apuntando a la base de datos.</p><p>Creamos un archivo llamado <code>data-source.ts</code> donde usaremos la clase <code>DataSource</code> de typeorm. Adicionalmente importaremos todas las clases que representen un modelo de datos de las entidades del proyecto. Finalizamos el archivo exportando nuestro objeto <code>AppDataSource</code> con la configuraci&#xF3;n de nuestra fuente de datos.</p><p>Al ser un proyecto en TypeScript, tambi&#xE9;n se debe configurar el archivo <code>tsconfig.json</code>, el cu&#xE1;l depender&#xE1; de cada proyecto o de tus preferencias en este framework.</p><p>Y por &#xFA;ltimo creamos nuestros archivos de migraciones, los cuales llevan un formato parecido al de Sequelize: <code>&lt;timestamp&gt;-&lt;NombreMigracion&gt;.ts</code> (n&#xF3;tese el uso del formato <em>Pascal Case</em>).</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript">import { MigrationInterface, QueryRunner } from &quot;typeorm&quot;

export class PostRefactoringTIMESTAMP implements MigrationInterface {
    async up(queryRunner: QueryRunner): Promise&lt;void&gt; {
      // // l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n
    }

    async down(queryRunner: QueryRunner): Promise&lt;void&gt; {
      // l&#xF3;gica para revertir los cambios
    }
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>up</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>down</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en TypeORM.</span></p></figcaption></figure><p>Otra forma de crear este archivo es a trav&#xE9;s del cli usando el comando <code>typeorm migration:create ./path-to-migrations-dir/PostRefactoring</code>, el cual crear&#xE1; el archivo con el nombre de migraci&#xF3;n <code>PostRefactoring</code> usando el timestamp actual, para que puedas modificarlo a tu gusto.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://typeorm.io/migrations?ref=jorgetech.dev" rel="noreferrer"><em>TypeORM Migrations</em></a><em>.</em></p><h3 id="aplicativo-en-flask-python-usando-sqlalchemy-y-alembic">Aplicativo en Flask (Python) usando SQLAlchemy y Alembic</h3><p>Y para nuestro &#xFA;ltimo ejemplo creamos una aplicaci&#xF3;n desde cero en Flask usando las librer&#xED;as Flask, SQLAlchemy, Flask-SQLAlchemy, Alembic, Flask-Migrate y psycopg2-binary.</p><p>En este aplicativo tambi&#xE9;n podemos usar librer&#xED;as para el uso de variables de entorno como lo es python-dotenv, sin embargo, en nuestro ejemplo nos saltamos esta parte. De todas maneras recuerda mantener la seguridad de tu aplicativo usando variables de entorno y secretos para las credenciales de los aplicativos. Tambi&#xE9;n para el caso de Flask vamos a crear un archivo <code>config.py</code> donde exportamos la clase Config con las variables <code>SQLALCHEMY_DATABASE_URI = &apos;postgresql://postgres:password@localhost:5432/migrations_alembic&apos;</code> y <code>SQLALCHEMY_TRACK_MODIFICATIONS = False</code>.</p><p>El uso de Alembic para las migraciones tiene una l&#xF3;gica distinta a las de Sequelize y TypeORM: las migraciones no se crean a mano sino a trav&#xE9;s de cli (aunque &#xE9;stas &#xFA;ltimas tambi&#xE9;n tienen la tienen opci&#xF3;n, en Alembic es el m&#xE9;todo preferido). Entonces, en este caso usaremos el comando <code>flask db init</code> para inicializar nuestro sistema de migraciones, para luego usar el comando <code>flask db migrate -m &quot;Initial migration.&quot;</code> y por &#xFA;ltimo el comando <code>flask db upgrade</code>. En estos 3 pasos hemos inicializado la carpeta de migraciones (en el archivo app.py le hemos indicado al objeto <code>migrate</code> que usaremos el nombre de <code>migraciones</code> como carpeta).</p><p>En Alembic las migraciones van en la carpeta <code>versions</code> y no van enumeradas de menor a mayor a trav&#xE9;s del uso de timestamp o numeraci&#xF3;n con en las otras librerias, sino que se asigna una cadena hexadecimal de 12 car&#xE1;cteres o un &quot;12 digit hex code&quot;, y el orden de las versiones se indica a trav&#xE9;s de las variables <code>revision</code> y <code>down_revision</code> dentro de cada archivo (pudiendo complicar a simple vista el orden de las migraciones para nosotros los humanos).</p><p>Dentro de cada archivo se encuentra el c&#xF3;digo a ejecutar para subir o bajar de versi&#xF3;n.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&quot;&quot;&quot;Initial migration.

Revision ID: de3dad1e7bad
Revises: 
Create Date: 2024-06-26 12:26:49.288399

&quot;&quot;&quot;
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = &apos;de3dad1e7bad&apos;
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # l&#xF3;gica para transformaci&#xF3;n a nueva versi&#xF3;n


def downgrade():
    # l&#xF3;gica para revertir los cambios</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Ejemplo de m&#xE9;todos </span><code spellcheck="false" style="white-space: pre-wrap;"><span>upgrade</span></code><span style="white-space: pre-wrap;"> y </span><code spellcheck="false" style="white-space: pre-wrap;"><span>downgrade</span></code><span style="white-space: pre-wrap;"> de un archivo de migraci&#xF3;n en Sequelize.</span></p></figcaption></figure><p>Y listo, usamos el comando <code>flask db upgrade</code> para actualizar nuestra base de datos para tener nuestro proyecto en Flask al d&#xED;a.</p><p><em>Nota: m&#xE1;s informaci&#xF3;n acerca de esta librer&#xED;a la encuentras en el portal de </em><a href="https://flask-migrate.readthedocs.io/en/latest/?ref=jorgetech.dev" rel="noreferrer"><em>Flask-Migrate usando Alembic</em></a><em>.</em></p><h2 id="conclusiones">Conclusiones</h2><p>Las migraciones de bases de datos son esenciales para mantener la integridad y la funcionalidad de una aplicaci&#xF3;n en crecimiento. As&#xED; como el c&#xF3;digo de una aplicaci&#xF3;n evoluciona, tambi&#xE9;n lo debe hacer la estructura de su base de datos. Al implementar un enfoque estructurado para las migraciones, nos podemos asegurar que las actualizaciones y mejoras se realicen de manera controlada y reproducible, minimizando el riesgo de errores en producci&#xF3;n. Esto es especialmente crucial en entornos donde la precisi&#xF3;n y la consistencia son fundamentales, como en sistemas que manejan grandes vol&#xFA;menes de datos o que tienen requisitos regulatorios estrictos.</p><p>Incorporar tanto las funciones de &quot;upgrade&quot; como de &quot;downgrade&quot; en el proceso de migraci&#xF3;n no solo permite mejorar la base de datos, sino tambi&#xE9;n revertir cambios en caso de errores, as&#xED; como tambi&#xE9;n el uso de herramientas como Sequelize, TypeORM y Alembic hace que la creaci&#xF3;n y gesti&#xF3;n de migraciones sea m&#xE1;s accesible y menos propensa a fallas.</p><p>Y finalmente, adoptar un enfoque proactivo en la gesti&#xF3;n de migraciones refuerza el compromiso de un equipo de desarrollo con la calidad y la estabilidad del software. A medida que las aplicaciones crecen en complejidad y alcance, la capacidad de gestionar cambios en la base de datos de manera eficaz se convierte en un diferenciador clave.</p>]]></content:encoded></item><item><title><![CDATA[Configurar correos en Ghost]]></title><description><![CDATA[<p>TLDR: Cre&#xE9; mi primera p&#xE1;gina web para mi marca personal y me falt&#xF3; configurar el correo electr&#xF3;nico. Son 2 tipos de env&#xED;o de correo que usa Ghost: 1) emails transaccionales para registrarse en el portal o solicitud de correo para reinicio de</p>]]></description><link>https://jorgetech.dev/configurar-correos-en-ghost/</link><guid isPermaLink="false">6621330c6bfb5c1355dcad8d</guid><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Fri, 19 Apr 2024 20:32:46 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/003-feature-v2.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/003-feature-v2.png" alt="Configurar correos en Ghost"><p>TLDR: Cre&#xE9; mi primera p&#xE1;gina web para mi marca personal y me falt&#xF3; configurar el correo electr&#xF3;nico. Son 2 tipos de env&#xED;o de correo que usa Ghost: 1) emails transaccionales para registrarse en el portal o solicitud de correo para reinicio de contrase&#xF1;a (a trav&#xE9;s de SMTP), y 2) emails de marketing para el env&#xED;o de newsletter (a trav&#xE9;s de una API para correos masivos -bulk-). Intent&#xE9; configurar <a href="https://www.mailgun.com/?ref=jorgetech.dev" rel="noreferrer">Mailgun</a> como se recomienda en la <a href="https://ghost.org/docs/config/?ref=jorgetech.dev#mail" rel="noreferrer">documentaci&#xF3;n</a>, pero me salt&#xE9; un detalle y como tampoco quiero hacer mucho &#xE9;nfasis en el Newsletter, eleg&#xED; AWS SES como proveedor de env&#xED;o de correos y s&#xF3;lo dej&#xE9; configurado los env&#xED;os transaccionales.</p><h2 id="%C2%BFmailgun-o-aws-ses">&#xBF;Mailgun o AWS SES?</h2><p>Al momento de levantar un portal de Ghost, una parte f&#xE1;cil de dejar de &#xFA;ltimo es la elecci&#xF3;n de proveedor de env&#xED;o de correos, entonces &#xBF;qu&#xE9; significa esto?. Recuerda que tu portal tiene la opci&#xF3;n de crear usuarios para suscriptores o recibir un Newsletter apenas un post haya sido publicado.</p><p>La documentaci&#xF3;n de Ghost favorece Mailgun y explica que es muy f&#xE1;cil crear una cuenta, por lo que inicialmente elegir esta opci&#xF3;n suena muy f&#xE1;cil, y m&#xE1;s cu&#xE1;ndo te menciona que tiene una suscripci&#xF3;n de &quot;pay-as-you-go&quot;, la cual cobra por cada correo enviado. Y adicional suma al hecho de que Mailgun no cobra cuando la factura es menor a $ 0.50 en USD, por lo que podemos decir que tenemos alrededor de 600 env&#xED;os de correos de forma gratuita.</p><p>Pero parte de esta informaci&#xF3;n, para hoy 18 de abril de 2024, est&#xE1; un poco desactualizada.</p><p><mark>Actualmente no hay forma de elegir el plan &quot;pay-as-you-go&quot;, o no aparece entre las </mark><a href="https://www.mailgun.com/pricing/?ref=jorgetech.dev" rel="noreferrer"><mark>opciones de plan</mark></a><mark>, por lo que se debe empezar desde el m&#xE1;s econ&#xF3;mico de $ 35.00 para una capacidad m&#xE1;xima de 50,000 correos, lo cual no es gratis como podemos leer.</mark></p><p>Esto hace que Mailgun no suene tan favorable. Y como por ahora el env&#xED;o de Newsletter no es primordial, podemos obviarlo y configurar alg&#xFA;n servicio para s&#xF3;lo los correos transaccionales, lo cu&#xE1;l se explica m&#xE1;s adelante.</p><p>Por lo que respondiendo a esta pregunta: si quieres enviar todo tipo de correos, transaccionales y de marketing, <strong>configura Mailgun</strong> (incluso en la &#xFA;ltima secci&#xF3;n te explico como crear una cuenta &quot;pay-as-you-go&quot; actualmente), y si solo necesitas para el manejo de usuarios, solo correos transaccionales, entonces <strong>configura AWS SES</strong>, o Gmail, o Mailchimp, o cualquier otro servicio que desees.</p><h2 id="pero-%C2%BFqu%C3%A9-son-correos-transaccionales-y-qu%C3%A9-son-correos-de-marketing">Pero, &#xBF;qu&#xE9; son correos transaccionales y qu&#xE9; son correos de marketing?</h2><p>La administraci&#xF3;n de usuarios creados con el correo electr&#xF3;nico requiere enviar informaci&#xF3;n privada para verificar la direcci&#xF3;n o enviar un email para cambiar la contrase&#xF1;a con alg&#xFA;n token. Este tipo de comunicaci&#xF3;n son correos transaccionales y es parte vital de cualquier portal.</p><p>Cuando queremos enviar emails con una vista o invitaci&#xF3;n a leer el &#xFA;ltimo post, informaci&#xF3;n privada que es s&#xF3;lo para ciertos miembros, o simplemente enviar un correo para publicidad, a esto se le llama correo de marketing.</p><p>Ghost solo permite el servicio de Mailgun para el env&#xED;o de correos de marketing, y tienen varias razones <a href="https://ghost.org/docs/faq/mailgun-newsletters/?ref=jorgetech.dev" rel="noreferrer">explicadas en su portal</a>, y el TLDR: Mailgun usa mecanismos para evitar que los servidores de correos sean clasificados como spam.</p><h2 id="entonces-%C2%BFc%C3%B3mo-configuro-aws-ses">Entonces, &#xBF;c&#xF3;mo configuro AWS SES?</h2><p>Una vez decidido es simplemente entrar a nuestra cuenta de AWS, ir a la secci&#xF3;n de SES (Simple Email Service) y registrar nuestro dominio. En el post pasado explicaba el uso de AWS Route 53 y la adquisici&#xF3;n del dominio a trav&#xE9;s de Namecheap. Como la configuraci&#xF3;n de los servidores de Namecheap apunta a los servidores de AWS, espec&#xED;ficamente hacia los de Route 53, registrar el dominio en AWS SES es muy f&#xE1;cil, sobre todo porque al ser servicios de Amazon, de una vez los datos de los servidores y dem&#xE1;s son registrados de forma autom&#xE1;tica.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-103655.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1831" height="702" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-103655.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-103655.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-103655.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-103655.png 1831w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Crear una identidad para el dominio.</span></figcaption></figure><p>Una vez registrado, es s&#xF3;lo esperar que se haya verificado el dominio y listo, ya tenemos esta parte configurada, ya podemos enviar correos de parte de jorgetech.dev. Ahora nos queda probar, pero para eso, tenemos que verificar una cuenta de correo electr&#xF3;nico.</p><figure class="kg-card kg-image-card"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104207.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1850" height="820" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-104207.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-104207.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-104207.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104207.png 1850w" sizes="(min-width: 720px) 720px"></figure><p>AWS SES tambi&#xE9;n permite verificar correos electr&#xF3;nicos, comprobar que nos pertenece y enviar mensajes con el correo verificado como destinatario, pero esta no es la raz&#xF3;n de verificar el correo. Resulta que al registrar tu dominio, este a&#xFA;n contin&#xFA;a estando en un ambiente de Sandbox, o de pruebas, y solo es posible enviar correos a direcciones autorizadas o verificadas. Es m&#xE1;s un tema de seguridad que otra cosa, y la forma de salir de este ambiente y transformarlo a uno de producci&#xF3;n, es haciendo la solicitud a AWS. Recuerda que esto sube los topes de tus mensajes.</p><p>Teniendo registrado el dominio y con correo electr&#xF3;nico (puede ser uno personal), podemos enviar correos de prueba para verificar que todo est&#xE1; funcionando como deber&#xED;a. Una vez todo parece ir bien, lo que falta es solicitar salir del ambiente Sandbox.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104727.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1840" height="872" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-104727.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-104727.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-104727.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-104727.png 1840w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Registro de un correo electr&#xF3;nico como identidad.</span></figcaption></figure><h3 id="abrir-un-caso-en-aws-para-salir-de-ambiente-sandbox">Abrir un caso en AWS para salir de ambiente Sandbox</h3><p>Esta parte es la m&#xE1;s &quot;personal&quot;, y digo personal, porque es un correo que una persona debe verificar y autorizar, o eso parece.</p><p>Mientras est&#xE1;s en el Dashboard de SES puedes ver un cartel azul aparecer indicando abrir un caso solicitando la salida del ambiente Sandbox. En el caso, se debe seleccionar si el uso del correo es para transacciones o para marketing. La idea es colocar una descripci&#xF3;n de para qu&#xE9; vas a usar el correo, c&#xF3;mo piensas manejar la distribuci&#xF3;n de correos, c&#xF3;mo vas a manejar los usuarios, que proceso tienes para cuando un usuario no quiere recibir correos o el famoso bot&#xF3;n de &quot;unsubscribe&quot; y dem&#xE1;s.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105338.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1834" height="842" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-105338.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-105338.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-105338.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105338.png 1834w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Caso creado para la solicitud de salida de Sandbox.</span></figcaption></figure><p>Una vez creado este caso o ticket, te llegar&#xE1;n correos con la apertura de tu caso y probablemente te pidan m&#xE1;s detalles. Una vez transcurra un tiempo entre 24 y 48 horas, te responder&#xE1;n con los t&#xE9;rminos y condiciones y la aprobaci&#xF3;n de la salida de ambiente Sandbox.</p><p>A partir de aqu&#xED;, puedes eliminar tu correo personal como verificado y volver a probar desde SES. S&#xF3;lo queda pendiente la configuraci&#xF3;n Ghost.</p><h2 id="configuraci%C3%B3n-de-correo-en-ghost">Configuraci&#xF3;n de correo en Ghost</h2><p>Ac&#xE1; es sencillo, es ingresar a la instancia EC2 a trav&#xE9;s de SSH y buscar el archivo de configuraci&#xF3;n de ghost para cambiar algunos valores. Este documento est&#xE1; en la direcci&#xF3;n  <code>/opt/bitnami/ghost/config.production.json</code> y en el portal de Bitnami <a href="https://docs.bitnami.com/general/apps/ghost/configuration/configure-smtp/?ref=jorgetech.dev" rel="noreferrer">puedes encontrar m&#xE1;s informaci&#xF3;n de configuraci&#xF3;n</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105928.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1094" height="568" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-105928.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-105928.png 1000w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-105928.png 1094w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Archivo de configuraci&#xF3;n de Ghost.</span></figcaption></figure><p>Recuerda que este es el archivo JSON de configuraci&#xF3;n de Ghost para varias opciones, como las credenciales de la base de datos o la url de la p&#xE1;gina.</p><p>Luego, puedes ir al portal administrativo de Ghost e intentar ingresar al portal administrativo, dar click en el bot&#xF3;n de &quot;Forgot password&quot; y esperar que el correo de reinicio de contrase&#xF1;a llegue tu bandeja, y listo: ya tienes configurado tu sistema de correo en Ghost.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-110732.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1852" height="903" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-19-110732.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-19-110732.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-19-110732.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-19-110732.png 1852w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Solicitar correo de recuperaci&#xF3;n de contrase&#xF1;a.</span></figcaption></figure><h2 id="%C2%BFy-c%C3%B3mo-hago-para-crear-una-cuenta-mailgun-con-el-plan-pay-as-you-go">&#xBF;Y c&#xF3;mo hago para crear una cuenta Mailgun con el plan &quot;pay-as-you-go&quot;?</h2><p>Listo, entonces queremos crear una cuenta en Mailgun, pero no queremos pagar por un plan por una capacidad m&#xE1;xima de mensajes, sino pagar por lo que usamos. Tener disponible 50,000 mensajes para alguien que est&#xE1; empezando es demasiado.</p><p>En el portal de Ghost indica que se puede obtener una cuenta &quot;pay-as-you-go&quot;, pero no indica exactamente c&#xF3;mo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-4.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1436" height="884" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-4.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-4.png 1000w, https://jorgetech.dev/content/images/2024/04/image-4.png 1436w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Secci&#xF3;n &quot;Why do I have to set up Mailgun?&quot; de Ghost.</span></figcaption></figure><p>Alguien ya indic&#xF3; en los foros de Ghost que no es posible abrir una cuenta y seleccionar la opci&#xF3;n de pagar a medida que se usa, y preguntando qu&#xE9; opciones hab&#xED;an, se le respondi&#xF3; como hacer para obtener este plan: <mark>al momento de crear una nueva cuenta, llenar el formulario del m&#xE9;todo de pago, seleccionar un mes de prueba gratis, usar el servicio por unos d&#xED;as y luego darse de baja del plan, por lo que autom&#xE1;ticamente la plataforma te asigna en el plan de &quot;pay-as-you-go&quot;</mark>. De esta forma no se cobra por ning&#xFA;n plan y puedes usar el servicio pagando por los mensajes que env&#xED;es, pero si estos son menos de aproximadamente 600, puede que no se te facture al respecto.</p><p><a href="https://forum.ghost.org/t/solved-mailgun-has-changed-pay-as-you-go-offer-how-to-send-cheap-emails-with-mailgun/32850?ref=jorgetech.dev" rel="noreferrer">En este enlace</a> tienes toda la informaci&#xF3;n del caso y las respuestas que estamos mencionando.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-5.png" class="kg-image" alt="Configurar correos en Ghost" loading="lazy" width="1386" height="915" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-5.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-5.png 1000w, https://jorgetech.dev/content/images/2024/04/image-5.png 1386w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Capturas de pantalla de c&#xF3;mo obtener el plan &quot;pay-as-you-go&quot; de Mailgun.</span></figcaption></figure><p>El error que comet&#xED; fue que cre&#xE9; la cuenta directamente y ya no me es posible seleccionar una prueba por un tiempo o <em>trial</em>, por lo que debo cancelar mi cuenta, esperar un tiempo y volverla a crear con la opci&#xF3;n de prueba, usarla unos d&#xED;as (antes del corte) y luego cancelar la suscripci&#xF3;n para que se me asigne el plan que deseo.</p><p>En resumen, el portal ya se encuentra habilitado para suscribirse. Por los momentos no tengo un plan de Newsletter o secciones exclusivas para miembros, pero si deseo a&#xF1;adir esto en un futuro, dependiendo de como vayan las cosas.</p><p>Si tienes alguna duda de c&#xF3;mo hacer alguno de estos pasos, d&#xE9;jame saber y con seguridad podemos resolverlo. !Feliz d&#xED;a!</p>]]></content:encoded></item><item><title><![CDATA[Mi propio blog en Ghost]]></title><description><![CDATA[<p>TLDR: Quise crear mi propia p&#xE1;gina web tipo Blog, usando alguna tecnolog&#xED;a que fuera novedosa, descartando viejos conocidos en PHP como WordPress o Drupal. Tambi&#xE9;n quise implementar el proceso de levantamiento usando alg&#xFA;n servicio de c&#xF3;mputo en la nube como</p>]]></description><link>https://jorgetech.dev/mi-propio-blog-en-ghost/</link><guid isPermaLink="false">66105b82aa35cb4bdf8c2e94</guid><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Fri, 12 Apr 2024 20:52:00 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/ghost-blog.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/ghost-blog.png" alt="Mi propio blog en Ghost"><p>TLDR: Quise crear mi propia p&#xE1;gina web tipo Blog, usando alguna tecnolog&#xED;a que fuera novedosa, descartando viejos conocidos en PHP como WordPress o Drupal. Tambi&#xE9;n quise implementar el proceso de levantamiento usando alg&#xFA;n servicio de c&#xF3;mputo en la nube como AWS o Vercel. Termin&#xE9; eligiendo <a href="https://ghost.org/?ref=jorgetech.dev" rel="noreferrer">Ghost</a> como tecnolog&#xED;a, descartando el servicio que ellos ofrecen para los blogs y creando una instancia en AWS EC2. No sali&#xF3; como esperaba, tuve unos inconvenientes y me apoy&#xE9; con unas im&#xE1;genes ya existentes de <a href="https://bitnami.com/stack/ghost/cloud/aws?ref=jorgetech.dev" rel="noreferrer">Bitnami</a> para la puesta en servicio. Lo dem&#xE1;s fue configuraci&#xF3;n y aqu&#xED; estamos.</p><hr><h2 id="elecci%C3%B3n-de-ghost-razones-personales-puedes-saltar-esta-secci%C3%B3n">Elecci&#xF3;n de Ghost (razones personales, puedes saltar esta secci&#xF3;n)</h2><p>Primero, dentro de lo que quer&#xED;a hacer, investigu&#xE9; varias herramientas para creaci&#xF3;n de p&#xE1;ginas web (no solo blogs) que sean contenidas en un solo aplicativo (<em>backend</em> y <em>frontend</em> en el mismo despliegue) y que est&#xE9;n basadas en node.js.</p><p>En el mundo de node.js no hay muchas opciones, las m&#xE1;s relevantes son <em>Headless CMS</em>: <a href="https://strapi.io/?ref=jorgetech.dev" rel="noreferrer"><em>Strapi</em></a> y <a href="https://directus.io/?ref=jorgetech.dev" rel="noreferrer"><em>Directus</em></a>. Dos espectaculares opciones que llegan hasta ese punto, el de Headless.</p><p>Estas 2 opciones poseen todo lo que uno necesita para administrar contenido: manejo de usuarios, permisos, documentos, im&#xE1;genes, rutas, etc. Sin embargo, como su nombre indica, son <a href="https://aws.amazon.com/es/what-is/headless-cms/?ref=jorgetech.dev" rel="noreferrer"><em>Headless</em></a>, o lo que en la practica traduce, s&#xF3;lo es el backend o la administraci&#xF3;n del contenido. Para mi soluci&#xF3;n, quer&#xED;a desplegar usando un solo proyecto o repositorio, y a pesar de que pod&#xED;a iniciar un proyecto en <em>React.js</em> o <em>Next.js</em> con alguna librer&#xED;a como <a href="https://mui.com/?ref=jorgetech.dev" rel="noreferrer">MUI</a>, no quer&#xED;a llegar hasta ese punto o complicaci&#xF3;n.</p><p>Otro punto a tomar en cuenta, es que en el trabajo est&#xE1;bamos evaluando un cliente el cu&#xE1;l insist&#xED;a con un CMS completo, que a su preferencia, quer&#xED;a usar WordPress. Desde el departamento sabemos que estas tecnolog&#xED;as ahorran en esfuerzo a la hora del despliegue, pero dificultan (y en gigantes rangos) el proceso de integraci&#xF3;n y crecimiento propio de la soluci&#xF3;n, pero en nuestra opinion, <strong>esta es la principal raz&#xF3;n por la que a&#xFA;n no logramos superar PHP como lenguaje: proyectos completos como WordPress o Drupal nos enamoran para su uso, alej&#xE1;ndonos de lenguajes de programaci&#xF3;n m&#xE1;s eficientes, &#xF3;ptimos y con mejores curvas de aprendizaje</strong>.</p><p>Nunca hab&#xED;a escuchado Ghost, y a pesar de que al inicio tuve algunas confusiones acerca de c&#xF3;mo resuelve el contenido, me gust&#xF3; saber que:</p><ul><li>Es un CMS que permite generar p&#xE1;ginas y posts.</li><li>Posee una interfaz completa para la creaci&#xF3;n y edici&#xF3;n de contenido.</li><li>Est&#xE1; enfocado a p&#xE1;ginas por suscripci&#xF3;n, sin embargo, esto es opcional.</li><li>Posee Newsletter, lo cu&#xE1;l permite crear campa&#xF1;as de correos personalizadas.</li><li>La interfaz est&#xE1; impulsada por Handlebars, lo cu&#xE1;l es una tecnolog&#xED;a f&#xE1;cil de conocer.</li><li>Existen varios temas gratuitos y otros m&#xE1;s que pueden adquirirse por un precio, que complementan y mejora todo lo que Ghost puede hacer.</li><li>Las rutas, lenguajes, tags y otros elementos pueden personalizarse.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-3.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1523" height="910" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-3.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-3.png 1000w, https://jorgetech.dev/content/images/2024/04/image-3.png 1523w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Portal de Ghost.</span></figcaption></figure><p>Y queriendo usar alg&#xFA;n lenguaje de f&#xE1;cil uso para mi como node.js, est&#xE1; fue una elecci&#xF3;n f&#xE1;cil que me entusiasm&#xF3;. Pero igual, les recomiendo tomar en cuenta las contras:</p><ul><li>Manejo de un s&#xF3;lo Content-Type (posts).</li><li>No posee integraci&#xF3;n nativa con servicios de archivos o <em>media files</em> como AWS S3.</li></ul><p>De todas maneras, sigo pensando que es una buena soluci&#xF3;n para portales sencillos: blogs personales, p&#xE1;ginas de empresas, informaci&#xF3;n de podscasts o eventos, etc.</p><h2 id="elecci%C3%B3n-de-aws-tambi%C3%A9n-personal-puedes-continuar-con-la-siguiente-secci%C3%B3n">Elecci&#xF3;n de AWS (tambi&#xE9;n personal, puedes continuar con la siguiente secci&#xF3;n)</h2><p>Este es un tema un poco m&#xE1;s directo que la elecci&#xF3;n de Ghost. Durante mi carrera, he trabajado con uso de c&#xF3;mputo en la nube con los proveedores Azure y AWS. Cada uno tiene sus pros y contras, sin embargo, en mi d&#xED;a a d&#xED;a actual, es m&#xE1;s frecuente la elecci&#xF3;n de AWS debido a que su principal ventaja es la econom&#xED;a de precios (o eso aparenta).</p><p>Sin ahondar mucho, me gusta probar y saber qu&#xE9; y qu&#xE9; no se puede hacer con este servicio de c&#xF3;mputo en la nube, y con el ofrecimiento de 750 horas de c&#xF3;mputo gratis durante un a&#xF1;o, me parece una buena opci&#xF3;n.</p><p>El portal de Ghost ofrece un servicio de despliegue de tu portal (<a href="https://ghost.org/pricing/?ref=jorgetech.dev" rel="noreferrer">TryGhost</a>), con tu propio dominio, por $ 9.00, el cual es un buen precio a mi parecer, pero sigo pensando en que quiero desplegar mi propia soluci&#xF3;n.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-2.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1518" height="885" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-2.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-2.png 1000w, https://jorgetech.dev/content/images/2024/04/image-2.png 1518w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Opciones ofrecidas de servicios de Ghost en la nube.</span></figcaption></figure><p>Como dije al inicio, quer&#xED;a hacer un despliegue desde sus inicios, usar las tecnolog&#xED;as de AWS y aprovechar las 750 horas ofrecidas, por lo que de una vez tom&#xE9; esta opci&#xF3;n.</p><h2 id="primeros-pasos-y-fallos">Primeros pasos y fallos</h2><p>Bien, ya pong&#xE1;monos en modo programador y despleguemos nuestro propio portal.</p><p>Antes de iniciar, debo adquirir mi propio nombre de dominio o DNS (Domain Name System), que es c&#xF3;mo voy a ser conocido en el internet. Me dirig&#xED; a Namecheap y adquir&#xED; jorgetech.dev por la m&#xF3;dica suma de aproximadamente $ 11.00.</p><h3 id="crear-mi-primera-instancia-de-aws-ec2">Crear mi primera instancia de AWS EC2</h3><p>Listo, ahora simplemente creo una instancia en AWS EC2 con una imagen en Ubuntu y hago la instalaci&#xF3;n. Todos los pasos se encuentran en la p&#xE1;gina de Ghost. Ya hab&#xED;a &quot;jugado&quot; con esta tecnolog&#xED;a en local, por lo que simplemente era instalar en mi nueva m&#xE1;quina virtual, subir el tema de mi elecci&#xF3;n (desde la tienda o personalizado) y listo, &#xBF;suena f&#xE1;cil no?</p><p>En primer lugar, acced&#xED; a mi cuenta de AWS, me dirig&#xED; a la secci&#xF3;n de EC2 e inici&#xE9; una instancia micro (permitida para las 750 horas gratuitas) usando la imag&#xE9;n de Ubuntu que hab&#xED;a mencionado. En menos de 5 minutos, ya pod&#xED;a conectarme a trav&#xE9;s del protocolo SSH a mi m&#xE1;quina virtual (recuerden crear las credenciales necesarias para la conexi&#xF3;n).</p><p>Listo, entonces es solo ingresar y seguir los pasos:</p><ol><li>Crear un nuevo usuario.</li><li>Actualizar todos los paquetes.</li><li>Instalar NGINX.</li><li>Instalar MySQL.</li><li>Instalar Node.js.</li><li>Instalar Ghost-CLI.</li><li>Crear el directorio y habilitar los permisos para el usuario de la m&#xE1;quina.</li></ol><p>Y listo, ya solo falta el paso 8, instalar Ghost.</p><ol start="8"><li>Instalar Ghost con el comando <code>ghost install</code>.</li></ol><p>Aqu&#xED; es donde todo no correo como esperamos. Esta instalaci&#xF3;n nunca ocurre, la m&#xE1;quina virtual dura horas instalando Ghost hasta que se pierde la conexi&#xF3;n. Pens&#xE9; que era un problema de falta de memor&#xED;a f&#xED;sica (AWS EBS), por lo que repet&#xED; la creaci&#xF3;n de la instancia dejando a la m&#xE1;quina con 50 GB de memoria, pero a&#xFA;n as&#xED;, la instalaci&#xF3;n se congelaba.</p><h3 id="probando-con-otras-im%C3%A1genes-oficiales-de-aws">Probando con otras im&#xE1;genes oficiales de AWS</h3><p>Al momento de crear una instancia, tenemos otras opciones para la imagen, desde Linux, Debian, una versi&#xF3;n creada espec&#xED;ficamente por Amazon, hasta instalar Windows o MacOS.</p><p>Obviamente todos los intentos fueron con las distribuciones de Linux (donde los comandos no son siempre iguales), sin embargo, suced&#xED;a lo mismo: siempre se congelaba en el paso 4 o 5 de la instalaci&#xF3;n de Ghost.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/ghost-install.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1217" height="620" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/ghost-install.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/ghost-install.png 1000w, https://jorgetech.dev/content/images/2024/04/ghost-install.png 1217w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Mensajes del terminal al instalar Ghost. Aqu&#xED; es donde la instalaci&#xF3;n se congelaba.</span></figcaption></figure><p>Por lo que ya hab&#xED;a que recurrir a la mag&#xED;a del internet. Resulta que este problema, no era &#xFA;nico para mi. Como pueden ver en el post, varios usuarios reclamaban el mismo inconveniente, por lo que eran un mensaje en Github donde un usuario indicaba que este inconveniente era porque la m&#xE1;quina virtual s&#xF3;lo ten&#xED;a 1 GB de memoria RAM y se necesitaban m&#xED;nimo 2 GB. Esto era contradictorio a la p&#xE1;gina de Ghost, donde se indica que m&#xED;nimo, 1 GB de memoria RAM es suficiente.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jorgetech.dev/content/images/2024/04/image-1.png" class="kg-image" alt="Mi propio blog en Ghost" loading="lazy" width="1419" height="584" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/image-1.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/image-1.png 1000w, https://jorgetech.dev/content/images/2024/04/image-1.png 1419w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Captura de pantalla a fecha 9 de abril de 2024 donde se indican los requerimientos para instalaci&#xF3;n.</span></figcaption></figure><p>L&#xE1;stima, todo parec&#xED;a muy sencillo. Luego de esto, s&#xF3;lo quedaba la configuraci&#xF3;n de NGINX, el dominio y los certificados.</p><p>Esto fueron varios intentos, cada uno durando entre 1 hora y 3 horas. Al 3er d&#xED;a me rend&#xED; con esta opci&#xF3;n y empec&#xE9; a buscar alternativas.</p><p>Pude haber elegido Digital Ocean para el despliegue, o directamente adquirir el servicio desde la p&#xE1;gina de Ghost, sin embargo, quer&#xED;a insistir con esta opci&#xF3;n. Dudo mucho que hayan clientes peque&#xF1;os en mi trabajo para proponer esta opci&#xF3;n, pero quien sabe, nunca est&#xE1; de m&#xE1;s tener esto en el arsenal (al menos una landing page con redirecciones puede ser muy f&#xE1;cil para vender).</p><p>Y aqu&#xED; es cuando descubr&#xED; las bondades de desplegar una instancia usando im&#xE1;genes de terceros, como los el proveedor Bitnami, quien justamente para Ghost tienen una imagen de uso gratuito.</p><h2 id="tutorial-o-gu%C3%ADa-de-despliegue-de-ghost-en-aws-ec2-con-uso-de-una-imagen-de-bitnami">Tutorial o gu&#xED;a de despliegue de Ghost en AWS EC2 con uso de una imagen de Bitnami</h2><p>Vamos a seguir los pasos de manera general de como instalar Ghost en AWS usando una imagen de instancia de Bitnami:</p><ol><li>Ingresamos a nuestra cuenta de AWS, nos dirigimos a la secci&#xF3;n de EC2 y seleccionamos crear una instancia.</li><li>En la secci&#xF3;n de imagen, nos vamos a la secci&#xF3;n de im&#xE1;genes de terceros.</li><li>Buscamos Ghost de Bitnami y seleccionamos la que necesitamos.</li><li>Seleccionar un &quot;key pair&quot; para la instancia (para la conexi&#xF3;n a trav&#xE9;s de SSH).</li><li>Seleccionar un &quot;security group&quot; que permita conexiones desde cualquier IP (Anywhere).</li><li>Configurar unos 30 GB de storage EBS.</li><li>Esperamos por la finalizaci&#xF3;n del despliegue y luego nos vamos a los mensajes de Log de la instancia.</li><li>Buscamos la secci&#xF3;n donde se inicia el usuario y contrase&#xF1;a para ingresar. Este proceso es para obtener las credenciales para ingresar al aplicativo por primera vez: <a href="https://docs.bitnami.com/aws/faq/get-started/find-credentials/?ref=jorgetech.dev" rel="noreferrer">encuentra las credenciales de la aplicaci&#xF3;n</a>.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/get-started/?ref=jorgetech.dev" rel="noreferrer">Getting started</a> puedes obtener informaci&#xF3;n adicional de c&#xF3;mo ingresar a la base de datos o la configuraci&#xF3;n inicial de Apache.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/configuration/?ref=jorgetech.dev" rel="noreferrer">Configuration</a> hay informaci&#xF3;n adicional para otras configuraciones del servicio SMTP o Apache.</li><li>En la secci&#xF3;n de <a href="https://docs.bitnami.com/aws/apps/ghost/administration/?ref=jorgetech.dev" rel="noreferrer">Administration</a> se encuentra la mayor porci&#xF3;n de informaci&#xF3;n para el servidor en Ghost.<ol><li><a href="https://docs.bitnami.com/aws/apps/ghost/administration/control-services/?ref=jorgetech.dev" rel="noreferrer">Esta sub-secci&#xF3;n</a> te muestra los comandos para iniciar, reiniciar o detener los servicios de Ghost.</li><li><a href="https://docs.bitnami.com/aws/apps/ghost/administration/configure-domain/?ref=jorgetech.dev" rel="noreferrer">Sub-secci&#xF3;n a seguir</a> para configurar el dominio del servidor.</li></ol></li><li><a href="https://docs.bitnami.com/aws/faq/administration/generate-configure-certificate-letsencrypt/?ref=jorgetech.dev" rel="noreferrer">En esta gu&#xED;a</a> se encuentra la informaci&#xF3;n para configurar los certificados SSL y el DNS a trav&#xE9;s de HTTPS.</li><li>Crear una Elastic IP desde AWS y asignarla a nuestra instancia de Ghost.</li><li>Tambi&#xE9;n se debe configurar el DNS (para mi caso us&#xE9; Namecheap en la compra de mi dominio). Este proceso es sencillo y directo. Cuando est&#xE9;s configurando el dominio, ve a la secci&#xF3;n de &quot;Advanced DNS&quot; para el dominio y crea un &quot;A Record&quot;. Selecciona &quot;Host&quot; como &quot;@&quot; e introduce el Valor de la Elastic IP que configuraste antes. Guarda esto y crea un segundo &quot;A Record&quot;, selecciona &quot;Host&quot; como &quot;www&quot; y introduce el Valor para que sea el mismo del Elastic IP que configuraste antes.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145638.png" width="1857" height="922" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-145638.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-145638.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-145638.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145638.png 1857w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145745.png" width="1089" height="666" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-145745.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-145745.png 1000w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-145745.png 1089w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-150207.png" width="744" height="573" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-150207.png 600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-150207.png 744w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-151228.png" width="1831" height="873" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-151228.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-151228.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-151228.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-151228.png 1831w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152044.png" width="1792" height="900" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-152044.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-152044.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-152044.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152044.png 1792w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152842.png" width="1854" height="885" loading="lazy" alt="Mi propio blog en Ghost" srcset="https://jorgetech.dev/content/images/size/w600/2024/04/Screenshot-2024-04-12-152842.png 600w, https://jorgetech.dev/content/images/size/w1000/2024/04/Screenshot-2024-04-12-152842.png 1000w, https://jorgetech.dev/content/images/size/w1600/2024/04/Screenshot-2024-04-12-152842.png 1600w, https://jorgetech.dev/content/images/2024/04/Screenshot-2024-04-12-152842.png 1854w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Screenshots del proceso mientras se configura Ghost desde AWS.</span></p></figcaption></figure><p>Hay varios puntos y fuentes de informaci&#xF3;n adicional que dejar&#xE9; por ac&#xE1; para vuestro inter&#xE9;s:</p><ul><li><a href="https://github.com/bdeitte/ghost-on-aws?ref=jorgetech.dev" rel="noreferrer">Readme en Github</a> por el usuario <a href="https://github.com/bdeitte/ghost-on-aws/commits?author=bdeitte&amp;ref=jorgetech.dev" rel="noreferrer">bdeitte</a> donde explica el proceso de configuraci&#xF3;n de Ghost con AWS y Namecheap.</li><li><a href="https://stackoverflow.com/questions/77107313/installing-ghost-cms-self-hosting-on-aws-ec2-seems-to-never-complete?ref=jorgetech.dev" rel="noreferrer">Consulta en Stack Overflow</a> donde se concluye que para la instalaci&#xF3;n de Ghost se requieren 2 GB de memoria RAM en vez de 1 GB.</li></ul><p>Se que hay otros datos importantes, sin embargo, si presentas un inconveniente durante la instalaci&#xF3;n, d&#xE9;jame saber para ayudarte o guiarte durante el proceso.</p>]]></content:encoded></item><item><title><![CDATA[Próximamente]]></title><description><![CDATA[<p>Este es mi blog, de parte de Jorge. Es un nuevo lugar para todos esos temas, pensamientos, tutoriales, ejercicios, pruebas de concepto e historias que quiero compartir sobre la programaci&#xF3;n de aplicativos web, softwares, tecnolog&#xED;as y dem&#xE1;s.</p><p>Soy programador de aplicativos web, por lo</p>]]></description><link>https://jorgetech.dev/proximamente-jorgetech/</link><guid isPermaLink="false">65d1690e301abf0719e62299</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Jorge Tech]]></dc:creator><pubDate>Sun, 18 Feb 2024 02:18:54 GMT</pubDate><media:content url="https://jorgetech.dev/content/images/2024/04/proximamente.png" medium="image"/><content:encoded><![CDATA[<img src="https://jorgetech.dev/content/images/2024/04/proximamente.png" alt="Pr&#xF3;ximamente"><p>Este es mi blog, de parte de Jorge. Es un nuevo lugar para todos esos temas, pensamientos, tutoriales, ejercicios, pruebas de concepto e historias que quiero compartir sobre la programaci&#xF3;n de aplicativos web, softwares, tecnolog&#xED;as y dem&#xE1;s.</p><p>Soy programador de aplicativos web, por lo que hay muchas ideas que quiero que conozcan y tal vez si uno o varios aprenden algo al respecto, me sentir&#xE9; m&#xE1;s que conforme. Tambi&#xE9;n quiero mostrar de lo que soy capaz y tener un lugar donde demostrar todos mis conocimientos.</p><p>Esto apenas est&#xE1; empezando, as&#xED; que prep&#xE1;rate y recuerda, si quieres ver alg&#xFA;n tema en espec&#xED;fico, s&#xF3;lo d&#xE9;jame saber.</p>]]></content:encoded></item></channel></rss>