<?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[Tulio Calil Dev]]></title><description><![CDATA[Building Quality Technological Solutions]]></description><link>https://tuliocalil.com/</link><image><url>https://tuliocalil.com/favicon.png</url><title>Tulio Calil Dev</title><link>https://tuliocalil.com/</link></image><generator>Ghost 5.68</generator><lastBuildDate>Thu, 14 May 2026 19:12:34 GMT</lastBuildDate><atom:link href="https://tuliocalil.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Meu ultimo post / My last post]]></title><description><![CDATA[TRDL: Não vejo mais sentido em escrever postagens.]]></description><link>https://tuliocalil.com/my-last-post/</link><guid isPermaLink="false">6a06043c0d6afc01f8e0d339</guid><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Thu, 14 May 2026 17:41:18 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1529268209110-62be1d87fe75?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGdvb2RieWV8ZW58MHx8fHwxNzc4NzgwMzYxfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1529268209110-62be1d87fe75?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGdvb2RieWV8ZW58MHx8fHwxNzc4NzgwMzYxfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Meu ultimo post / My last post"><p><strong>TRDL:</strong> N&#xE3;o vejo mais sentido em escrever postagens.</p><p>2026 tem sido um ano bem diferente dos demais, eu nasci em 1995 e foi incr&#xED;vel ter visto a evolu&#xE7;&#xE3;o da tecnologia at&#xE9; aqui, eu consegui ver a internet e os computadores pessoais se popularizando (popularizando de verdade, acesso pra quem era baixa renda), vi os celulares saindo do famoso Nokia da cobrinha (<a href="https://oglobo.globo.com/economia/tres-celulares-da-extinta-nokia-que-marcaram-epoca-14501974?ref=tuliocalil.com" rel="noreferrer">1100</a>), passando pela revolu&#xE7;&#xE3;o absurda que foi os jogos em JAVA com a <a href="https://pt.wikipedia.org/wiki/Gameloft?ref=tuliocalil.com" rel="noreferrer">Gameloft </a>lan&#xE7;ando coisas que pareciam imposs&#xED;veis de rodar em um <a href="https://pt.wikipedia.org/wiki/Motorola_RAZR_V3?ref=tuliocalil.com" rel="noreferrer">Motorola V3</a> at&#xE9; as primeiras vers&#xF5;es do Android que me deixaram extasiado.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://media-cldnry.s-nbcnews.com/image/upload/mpx/2704722219/2024_02/1708692884426_tdy_news_7a_gosk_vintage_tech_240223_1920x1080-ssbp4k.jpg" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy"><figcaption><a href="https://www.today.com/video/from-ipods-to-flip-phones-vintage-tech-is-becoming-all-the-rage-204776005736?ref=tuliocalil.com"><span style="white-space: pre-wrap;">https://www.today.com/video/from-ipods-to-flip-phones-vintage-tech-is-becoming-all-the-rage-204776005736</span></a></figcaption></figure><p>Al&#xE9;m de tudo isso, tivemos avan&#xE7;os incr&#xED;veis; eu me apaixonava cada vez mais por tecnologia e pela inova&#xE7;&#xE3;o que aparecia ano ap&#xF3;s ano; era como se a cada ano a gente tivesse um mundo novo para desbravar: Mp3, Mp4, smartphones, jogos, redes sociais, softwares incr&#xED;veis, novas formas de se comunicar, de se expressar, enfim, novas formas de passar pela vida, agora de forma digital.</p><p>Mas 2026 tem sido um ano bem diferente dos demais; eu crio conte&#xFA;do desde 2012, na realidade, talvez at&#xE9; antes disso, em f&#xF3;runs que hoje nem existem mais. Sempre gostei de compartilhar conhecimento, de ajudar outras pessoas em dificuldades que eu tive, de tentar encurtar o caminho que, pra mim, foi &#xE1;rduo e desafiador. Isso me fazia bem; eu sentia satisfa&#xE7;&#xE3;o em cada letra digitada. Mesmo sem ter grandes n&#xFA;meros, eu ainda gostava disso.</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2026/05/image.png" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy" width="641" height="429" srcset="https://tuliocalil.com/content/images/size/w600/2026/05/image.png 600w, https://tuliocalil.com/content/images/2026/05/image.png 641w"></figure><p>Em 30 de novembro de 2022, o mundo come&#xE7;ou a mudar: o lan&#xE7;amento do pol&#xEA;mico ChatGPT, que, embora n&#xE3;o tenha sido a primeira LLM criada ou aberta, foi a que caiu na gra&#xE7;a do p&#xFA;blico. Uma interface amig&#xE1;vel, resultados r&#xE1;pidos que surpreendiam na hora, mas tamb&#xE9;m, sendo a maior ironia da tecnologia: o Google inventou o motor (o Transformer), mas a OpenAI foi quem construiu a Ferrari. Sem o paper &quot;<a href="https://arxiv.org/abs/1706.03762?ref=tuliocalil.com" rel="noreferrer">Attention Is All You Need</a>&quot; do Google, o &apos;T&apos; do ChatGPT (que significa Transformer) nem existiria.</p><figure class="kg-card kg-image-card"><img src="https://preview.redd.it/the-tweet-that-started-it-all-v0-7i1bf23bgmof1.png?auto=webp&amp;s=9685285fbdbd2a3e541e413e9c2cc8a7627a2846" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy"></figure><p>Confesso que por um bom tempo eu tentei ignorar, n&#xE3;o por quest&#xF5;es t&#xE3;o nobres quanto o impacto intelectual, educacional, ambiental e as controv&#xE9;rsias de direitos autorais que esses modelos causam de forma negativa, mas sim por que eu nunca gostei da qualidade do que sa&#xED;a dali, tudo era bem ruim no in&#xED;cio e hoje em dia temos modelos melhores, embora parecem ter a qualidade sazonalmente alterada, n&#xE3;o me entenda mal, n&#xE3;o estou dizendo que s&#xE3;o 100% melhores agora, ainda s&#xE3;o ruins, meu ponto sempre foi que LLM &#xE9; uma grande coisa (eu n&#xE3;o consegui pensar em um termo) que potencializa suas cren&#xE7;as, principalmente as ruins.</p><figure class="kg-card kg-image-card"><img src="https://shawnhymel.com/wp-content/uploads/2025/05/claude-4-refactor-thumbnail.png" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy"></figure><p>Hoje em dia, LLM faz parte do meu dia a dia. Consumo muito, principalmente para pesquisas; o que antes me gerava horas de busca no Google, em f&#xF3;runs e na documenta&#xE7;&#xE3;o, hoje acaba virando uma mensagem de &quot;busca pra mim tal coisa&quot; e retorna com a fonte&quot;. Embora eu ainda acesse conte&#xFA;dos diretamente, a frequ&#xEA;ncia &#xE9; baix&#xED;ssima. Minha quantidade de leitores diminuiu bastante tamb&#xE9;m. Afinal, com vibe coding hoje em dia, &#xE9; menos prov&#xE1;vel que algu&#xE9;m leia um post de &quot;Como fazer isso&quot; ou &quot;Melhores pr&#xE1;ticas pra aquilo&quot;. A internet foi inundada de conte&#xFA;do gerado por IA tamb&#xE9;m, o LinkedIn virou um grande palco pra isso, aqueles text&#xF5;es de &quot;N&#xE3;o &#xE9; sobre isso &#xE9; sobre aquilo&quot; virou at&#xE9; meme. No geral, s&#xE3;o conte&#xFA;dos bem pobres; tenho quase certeza de que quem pediu pra LLM escrever aquilo nem leu nem tentou passar sua personalidade pro texto.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://media.licdn.com/dms/image/v2/D4D22AQEEhdNQYZVC3Q/feedshare-shrink_800/feedshare-shrink_800/0/1731018122720?e=2147483647&amp;v=beta&amp;t=yhp_aWbhuAUUIObgMPL8LNAPEsT60ZlELjC6-D5cE4I" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy"><figcaption><a href="https://www.linkedin.com/posts/dorothykawira_have-you-all-seen-those-linkedin-memes-share-7260416238714011650-KiXm/?ref=tuliocalil.com"><span style="white-space: pre-wrap;">https://www.linkedin.com/posts/dorothykawira_have-you-all-seen-those-linkedin-memes-share-7260416238714011650-KiXm/</span></a></figcaption></figure><p>Esse &#xE9; s&#xF3; um adeus longo, n&#xE3;o tenho vontade de voltar a produzir novos textos ou mesmo v&#xED;deos, que j&#xE1; n&#xE3;o fazia h&#xE1; muito tempo, e n&#xE3;o me parece que o p&#xFA;blico se importa com isso agora, agrade&#xE7;o a quem leu at&#xE9; aqui, agrade&#xE7;o a quem me incentivava a fazer as postagens, agrade&#xE7;o a quem me seguia e lia os conte&#xFA;dos, espero que todos fiquem bem.</p><h3 id="b%C3%B4nus">B&#xF4;nus</h3><p>Alguns posts que nunca viram a luz do dia porque eu desanimei durante a cria&#xE7;&#xE3;o deles:</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2026/05/image-1.png" class="kg-image" alt="Meu ultimo post / My last post" loading="lazy" width="761" height="683" srcset="https://tuliocalil.com/content/images/size/w600/2026/05/image-1.png 600w, https://tuliocalil.com/content/images/2026/05/image-1.png 761w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item><item><title><![CDATA[Lynx - React Native alternative from TikTok]]></title><description><![CDATA[<p>Today(05/03/2025), ByteDance, the company behind TikTok, has launched <a href="https://lynxjs.org/index.html?ref=tuliocalil.com" rel="noreferrer"><strong>Lynx</strong></a>, a new framework that aims to revolutionize mobile app development, positioning itself as an alternative to React Native.</p><p>Lynx allows developers to build native interfaces using markup and CSS, making it easier to transition into mobile development without</p>]]></description><link>https://tuliocalil.com/lynxjs-react-native-alternative-from-tiktok/</link><guid isPermaLink="false">67c8818289d448021fe91c6b</guid><category><![CDATA[Mobile]]></category><category><![CDATA[React Native]]></category><category><![CDATA[News]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Wed, 05 Mar 2025 21:29:08 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1539242561050-387ee3928095?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGx5bnh8ZW58MHx8fHwxNzQxMjA5MzYxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1539242561050-387ee3928095?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGx5bnh8ZW58MHx8fHwxNzQxMjA5MzYxfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Lynx - React Native alternative from TikTok"><p>Today(05/03/2025), ByteDance, the company behind TikTok, has launched <a href="https://lynxjs.org/index.html?ref=tuliocalil.com" rel="noreferrer"><strong>Lynx</strong></a>, a new framework that aims to revolutionize mobile app development, positioning itself as an alternative to React Native.</p><p>Lynx allows developers to build native interfaces using markup and CSS, making it easier to transition into mobile development without needing to learn new languages or paradigms. Additionally, it emphasizes responsible use of the main thread to ensure improved interactivity and performance in applications.</p><p>In this post, we&apos;ll explore <a href="https://lynxjs.org/index.html?ref=tuliocalil.com" rel="noreferrer"><strong>Lynx</strong></a> by creating a simple login screen and sharing our thoughts on this new tool.</p><p>Lynx follows a concept similar to <strong>React Native</strong>, enabling developers to build native interfaces using a declarative approach. However, its key differentiator is its foundation in <strong>Rust</strong>, which powers the compilation process and other core functionalities, ensuring better <strong>performance and efficiency</strong>.</p><p>Another major highlight is its <strong>multi-platform support from day one</strong>. With a <strong>single codebase</strong>, Lynx promises <strong>native deployments</strong> for <strong>Android, iOS, and Web</strong>, providing a powerful solution for developers looking to achieve <strong>high performance and scalability</strong> across different platforms.</p><h2 id="starting-a-new-project">Starting a New Project</h2><p>Creating a new project feels a bit unusual, but it&apos;s not painful. We just need to download the app (APK or IPA) and use it to consume the bundle, similar to Expo Go applications. I believe this is only temporary.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="900" height="791" srcset="https://tuliocalil.com/content/images/size/w600/2025/03/image.png 600w, https://tuliocalil.com/content/images/2025/03/image.png 900w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Lynx iOS setup</span></figcaption></figure><p>The CLI is very simple; we just answer a few questions and get the project set up.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-3.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="602" height="337" srcset="https://tuliocalil.com/content/images/size/w600/2025/03/image-3.png 600w, https://tuliocalil.com/content/images/2025/03/image-3.png 602w"><figcaption><span style="white-space: pre-wrap;">Lynx project</span></figcaption></figure><p>After following all the steps to create the project (using Bun and Offshore), we got this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-1.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="401" height="612"><figcaption><span style="white-space: pre-wrap;">Ignore the LynxExplorer things, I just download the App to the project folder.</span></figcaption></figure><p>Checking the <code>package.json</code> its is very small:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-2.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="724" height="911" srcset="https://tuliocalil.com/content/images/size/w600/2025/03/image-2.png 600w, https://tuliocalil.com/content/images/2025/03/image-2.png 724w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Package.json</span></figcaption></figure><p>Now, to run the project is very simple, just run:</p><pre><code class="language-sh">bun run dev</code></pre><p>Open the Lynx explorer App, copy the terminal bundle URL and past on the app and hit &quot;Go&quot;.</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2025/03/image-4.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="452" height="948"></figure><p>The &quot;Hello, World!&quot; page is very simple and quite similar to a React Native app (which is obvious, since React Native also uses React):</p><pre><code class="language-jsx">import { useCallback, useEffect, useState } from &apos;@lynx-js/react&apos;

import &apos;./App.css&apos;
import arrow from &apos;./assets/arrow.png&apos;
import lynxLogo from &apos;./assets/lynx-logo.png&apos;
import reactLynxLogo from &apos;./assets/react-logo.png&apos;

export function App() {
  const [alterLogo, setAlterLogo] = useState(false)

  useEffect(() =&gt; {
    console.info(&apos;Hello, ReactLynx&apos;)
  }, [])

  const onTap = useCallback(() =&gt; {
    &apos;background only&apos;
    setAlterLogo(!alterLogo)
  }, [alterLogo])

  return (
    &lt;view&gt;
      &lt;view className=&apos;Background&apos; /&gt;
      &lt;view className=&apos;App&apos;&gt;
        &lt;view className=&apos;Banner&apos;&gt;
          &lt;view className=&apos;Logo&apos; bindtap={onTap}&gt;
            {alterLogo
              ? &lt;image src={reactLynxLogo} className=&apos;Logo--react&apos; /&gt;
              : &lt;image src={lynxLogo} className=&apos;Logo--lynx&apos; /&gt;}
          &lt;/view&gt;
          &lt;text className=&apos;Title&apos;&gt;React&lt;/text&gt;
          &lt;text className=&apos;Subtitle&apos;&gt;on Lynx&lt;/text&gt;
        &lt;/view&gt;
        &lt;view className=&apos;Content&apos;&gt;
          &lt;image src={arrow} className=&apos;Arrow&apos; /&gt;
          &lt;text className=&apos;Description&apos;&gt;Tap the logo and have fun!&lt;/text&gt;
          &lt;text className=&apos;Hint&apos;&gt;
            Edit&lt;text style={{ fontStyle: &apos;italic&apos; }}&gt;{&apos; src/App.tsx &apos;}&lt;/text&gt;
            to see updates!
          &lt;/text&gt;
        &lt;/view&gt;
        &lt;view style={{ flex: 1 }}&gt;&lt;/view&gt;
      &lt;/view&gt;
    &lt;/view&gt;
  )
}
</code></pre><p>We can see some differences here. Let&apos;s take a look:</p><h4 id="css-import"><strong>CSS Import:</strong></h4><p>Lynx has excellent CSS support, including gradients, animations, variables, themes, transitions, masking, and much more. It seems they support 100% of the current CSS properties and functionalities.</p><h4 id="hooks-from-lynx-jsreact"><strong>Hooks from <code>@lynx-js/react</code>:</strong></h4><p>I don&#x2019;t have much information about this, but on the Lynx React page, they mention that it&#x2019;s &quot;the package that provides the ReactLynx framework. ReactLynx not only delivers the ultra-high performance and cross-platform usability of the Lynx engine, but also has compatibility with React 17+ and the vast ecosystem of the React community.&quot;<br>So, I believe it wraps the React library.</p><h4 id="the-background-only-directive"><strong>The <code>background only</code> directive:</strong></h4><p>This is pretty cool! Lynx actually has two directives: <a href="https://lynxjs.org/api/react/document.directives?ref=tuliocalil.com#background-only" rel="noreferrer"><code>background</code></a> and <code>main thread</code>. As the name suggests, <code>background only</code> executes the function in another thread, making it useful for listeners. By using this, we can also reduce the bundle size. <a href="https://lynxjs.org/api/react/document.directives?ref=tuliocalil.com#background-only" rel="noreferrer">Check out the full documentation</a> for a better understanding.</p><h4 id="the-main-thread-directive"><strong>The <code>main thread</code> directive:</strong></h4><p>This directive is not used on the app page, but I&apos;ll talk about it anyway. It forces the code to run on the main thread, as expected, and is very useful for smooth animations and gesture handlers. It is part of the &quot;<a href="https://lynxjs.org/react/main-thread-script.html?ref=tuliocalil.com" rel="noreferrer">main thread scripts</a>&quot; which are commonly used for animations and gestures. According to the documentation:<br><em>&quot;It is primarily used to address the response delay issue in Lynx&apos;s multi-threaded architecture, aiming to achieve a near-native interactive experience.&quot;</em></p><h4 id="html-tags-without-pascalcase"><strong>HTML tags without PascalCase:</strong></h4><p>Lynx focuses on web technologies with greater fidelity than React Native, and we can see that here. Some core components (<code>view</code>, <code>image</code>, and <code>text</code>) are available by default (<a href="https://lynxjs.org/api/elements/built-in/view.html?ref=tuliocalil.com" rel="noreferrer">check all core components here</a>), and there is no need to import them. Interestingly, they are written in lowercase (flatcase?).</p><h4 id="the-bindtap-prop"><strong>The <code>bindtap</code> prop:</strong></h4><p>Here we have a slight difference in attribute naming. <code>bindtap</code> works like <code>onPress</code>, a function that returns a touch event.</p><p>This was a quick overview of the page. Now, let&apos;s take a look at the styles and see how they created the logo animation. (It shakes, and when clicked, it switches to React Native and starts spinning.)</p><pre><code class="language-css">:root {
  background-color: #000;
  --color-text: #fff;
}

.Background {
  position: fixed;
  background: radial-gradient(
    71.43% 62.3% at 46.43% 36.43%,
    rgba(18, 229, 229, 0) 15%,
    rgba(239, 155, 255, 0.3) 56.35%,
    #ff6448 100%
  );
  box-shadow: 0px 12.93px 28.74px 0px #ffd28db2 inset;
  border-radius: 50%;
  width: 200vw;
  height: 200vw;
  top: -60vw;
  left: -14.27vw;
  transform: rotate(15.25deg);
}

.App {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

text {
  color: var(--color-text);
}

.Banner {
  flex: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.Logo {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
}

.Logo--react {
  width: 100px;
  height: 100px;
  animation: Logo--spin infinite 20s linear;
}

.Logo--lynx {
  width: 100px;
  height: 100px;
  animation: Logo--shake infinite 0.5s ease;
}

@keyframes Logo--spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes Logo--shake {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(0.9);
  }
  100% {
    transform: scale(1);
  }
}

.Content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.Arrow {
  width: 24px;
  height: 24px;
}

.Title {
  font-size: 36px;
  font-weight: 700;
}

.Subtitle {
  font-style: italic;
  font-size: 22px;
  font-weight: 600;
  margin-bottom: 8px;
}

.Description {
  font-size: 20px;
  color: rgba(255, 255, 255, 0.85);
  margin: 15rpx;
}

.Hint {
  font-size: 12px;
  margin: 5px;
  color: rgba(255, 255, 255, 0.65);
}
</code></pre><p>It&apos;s awesome! We can apply a gradient background directly in CSS, create animations with keyframes (hello to my friend <code>animate.css</code>), and use variables in a native <code>.css</code> file! It&apos;s really exciting.</p><h2 id="lets-code">Lets Code</h2><p>Okay, now I&#x2019;m going to talk about my experience creating a simple login page with Lynx. I can start by saying: it&apos;s very fast and easy to code.<br><br>I&#x2019;ll create a Spotify-like login page. I found this design on Figma and thought it would be a great example to test.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-6.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="1205" height="535" srcset="https://tuliocalil.com/content/images/size/w600/2025/03/image-6.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/03/image-6.png 1000w, https://tuliocalil.com/content/images/2025/03/image-6.png 1205w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://www.figma.com/community/file/1112721959247599576?ref=tuliocalil.com"><span style="white-space: pre-wrap;">https://www.figma.com/community/file/1112721959247599576https://www.figma.com/community/file/1112721959247599576</span></a></figcaption></figure><p>I exported the images and imported into the project folder:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-7.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="235" height="178"><figcaption><span style="white-space: pre-wrap;">Images files</span></figcaption></figure><p>I am created two components only, a Toast (only to show the login success message) and a Button:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-8.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="173" height="75"><figcaption><span style="white-space: pre-wrap;">Project components</span></figcaption></figure><pre><code class="language-jsx">import &quot;../App.css&quot;;

export function Toast({ text }: { text: string }) {
  return (
    &lt;view className=&quot;Toast&quot;&gt;
      &lt;text&gt;{text}&lt;/text&gt;
    &lt;/view&gt;
  );
}
</code></pre><p>The Toast component is very simple; it&apos;s just a view component with text inside. The CSS class <code>.Toast</code> is where the magic happens:</p><pre><code class="language-css">.Toast {
  position: fixed;
  top: 10%;
  left: 50%;
  transform: translateX(-50%);
  width: 80%;
  max-width: 300px;
  background-color: #4caf50;
  color: white;
  text-align: center;
  padding: 15px;
  border-radius: 10px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  animation: slideDown 0.5s ease-out, fadeOut 0.5s ease-in 3s forwards;
  z-index: 1000;
}

@keyframes slideIn {
  from {
    transform: translate(-50%, -100%);
    opacity: 0;
  }
  to {
    transform: translate(-50%, 0);
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  to {
    opacity: 0;
    transform: translate(-50%, -100%);
  }
}
</code></pre><p>It&apos;s really cool to use CSS to create mobile components&#x2014;it feels like the PhoneGap/Cordova era.</p><p>The Button component has more features, but nothing new compared to React Native, except for the CSS classes:</p><pre><code class="language-jsx">import &quot;../App.css&quot;;

export function Button({
  onTap,
  text,
  image,
  type,
}: {
  onTap: () =&gt; void;
  text: string;
  image?: string;
  type?: &quot;primary&quot; | &quot;secondary&quot; | &quot;ghost&quot;;
}) {
  return (
    &lt;view className={`Button--container ${type}`} bindtap={onTap}&gt;
      {image &amp;&amp; (
        &lt;view class=&quot;Button--image-container&quot;&gt;
          &lt;image src={image} className=&quot;Button--image&quot; /&gt;
        &lt;/view&gt;
      )}
      &lt;view className=&quot;Button--text-container&quot;&gt;
        &lt;text className=&quot;Button--text&quot;&gt;{text}&lt;/text&gt;
      &lt;/view&gt;
    &lt;/view&gt;
  );
}
</code></pre><p>I created some props (<code>onTap</code>, <code>text</code>, <code>image</code>, and <code>type</code>) to make the component more extensible. The only noteworthy part here is the CSS classes. I used template strings to concatenate <code>Button--container</code> with the <code>type</code> value. It could be improved, but...</p><p>On the CSS part:</p><figure class="kg-card kg-code-card"><pre><code class="language-css">.Button--container {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: 0px 20px;
  width: 85vw;
  background-color: var(--button-background);
  height: 50px;
  border-radius: 100px;
  margin: 5px;
  border: var(--button-border-size) solid;
  border-color: var(--button-border);
}

.Button--image-container {
  display: flex;
  justify-content: flex-start;
}

.Button--image {
  width: 30px;
  height: 24px;
}

.Button--text-container {
  display: flex;
  width: 100%;
  justify-content: center;
}

.Button--text {
  font-size: 17px;
  font-weight: 700;
  color: var(--button-text);
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">CSS For the button</span></p></figcaption></figure><p>Nothing new here&#x2014;I used CSS variables to manage the variants.</p><p>So, the final application looks like this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-9.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="452" height="952"><figcaption><span style="white-space: pre-wrap;">Okay, the phone icon is stretched</span></figcaption></figure><p></p><h2 id="dev-tool">Dev tool</h2><p>Lynx has a dev tool similar to React/RN for debugging the application, with some cool additional features.</p><p>Unfortunately, I couldn&apos;t find the network tab to inspect requests.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/03/image-5.png" class="kg-image" alt="Lynx - React Native alternative from TikTok" loading="lazy" width="1360" height="995" srcset="https://tuliocalil.com/content/images/size/w600/2025/03/image-5.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/03/image-5.png 1000w, https://tuliocalil.com/content/images/2025/03/image-5.png 1360w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Lynx Dev Tool</span></figcaption></figure><h2 id="my-impressions-of-lynx">My Impressions of Lynx </h2><p>Lynx is truly amazing! The fact that it supports animations, transitions, gradients, and all the other CSS features that make the web so beautiful and fast &#x2014; but are not natively supported by React Native &#x2014; is already a huge advantage!</p><p>Additionally, the directives are one of the coolest aspects, something that stands out even more here than in React Native. And, of course, we can&#x2019;t overlook the power and speed of Rust, which makes <strong>hot reload</strong> and the build process incredibly efficient.</p><p>During these first minutes of development, I only missed two things:</p><ol><li><strong>Console logs</strong> &#x2013; The logs (<code>console.log</code>) don&#x2019;t appear in the terminal. I&#x2019;m not sure if this is an issue with the version I&#x2019;m using, but I could only see them through the Lynx Dev Tool.</li><li><strong>Inconsistent Hot Reload</strong> &#x2013; In some cases, especially when editing CSS and commenting out parts of the code to remove properties, the hot reload stopped working. I had to close and reopen the app to fix it.</li></ol><p>Another thing that felt a bit odd was the project startup process. Copying a URL and opening it in their app is somewhat unconventional. It&#x2019;s not a big deal, but it does feel unusual. Also, the app doesn&#x2019;t save the last bundle&#x2019;s URL, something <strong>Expo Go</strong> handles really well. I imagine this will be improved soon.</p><p>Overall, <strong>I really liked Lynx</strong>! The development experience was super smooth, and to be honest, I&#x2019;d love to test it in a <strong>real production app </strong>&#x1F929;.</p><p>There are still many features I&#x2019;m excited to explore, especially the <strong>native integration and compatibility with other libraries</strong>. I&#x2019;ll keep testing and sharing everything here on the blog! So, <strong>subscribe to the newsletter</strong> to get updates as soon as I post &#x2014; no spam, I promise! &#x1F680;</p><div class="kg-card kg-signup-card kg-width-wide " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for Tulio Calil Dev</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Building Quality Technological Solutions</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div><p>As usual, here&#x2019;s the repository with the code I wrote for this post:As usual, here&#x2019;s the repository with the code I wrote for this post:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tuliocll/lynx-spotify-login-demo?ref=tuliocalil.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - tuliocll/lynx-spotify-login-demo: A Spotify login page clone built with Lynx, exploring CSS support, animations, and performance.</div><div class="kg-bookmark-description">A Spotify login page clone built with Lynx, exploring CSS support, animations, and performance. - tuliocll/lynx-spotify-login-demo</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" alt="Lynx - React Native alternative from TikTok"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">tuliocll</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/ad88ccf645c698365ab86853d958627ee9627f532fdcf13667cd1f1f30e6ff01/tuliocll/lynx-spotify-login-demo" alt="Lynx - React Native alternative from TikTok"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Simplify API Testing and Collection Versioning Using httpYac]]></title><description><![CDATA[<p>As a developer, working with APIs can be both empowering and challenging. Whether you&apos;re building, testing, or debugging APIs, having the right tools at your disposal can make all the difference. While tools like <strong>Postman</strong> and <strong>Insomnia</strong> have become go-to solutions for API testing, there&#x2019;s a</p>]]></description><link>https://tuliocalil.com/simplify-api-testing-and-collection-versioning-using-httpyac/</link><guid isPermaLink="false">6794271c89d448021fe91a26</guid><category><![CDATA[Backend]]></category><category><![CDATA[Ferramentas]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Fri, 24 Jan 2025 23:55:49 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1598608146103-2eec9e333618?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExfHxnbnV8ZW58MHx8fHwxNzM3NzYyOTIyfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1598608146103-2eec9e333618?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDExfHxnbnV8ZW58MHx8fHwxNzM3NzYyOTIyfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Simplify API Testing and Collection Versioning Using httpYac"><p>As a developer, working with APIs can be both empowering and challenging. Whether you&apos;re building, testing, or debugging APIs, having the right tools at your disposal can make all the difference. While tools like <strong>Postman</strong> and <strong>Insomnia</strong> have become go-to solutions for API testing, there&#x2019;s a &quot;new&quot; player in town that&#x2019;s gaining traction for its simplicity and powerful features: <a href="https://httpyac.github.io/?ref=tuliocalil.com" rel="noreferrer"><strong>httpYac</strong></a>.</p><p>In this post, we&#x2019;ll dive into how <strong>httpYac&#x2019;s extension(VScode)</strong> can help you. From sending requests and testing endpoints to versioning your collections for better maintainability, httpYac offers a seamless experience for developers of all levels. Whether you&apos;re a seasoned API developer or just getting started, this guide will show you how to leverage httpYac to simplify your API testing process and keep your collections organized and version-controlled.</p><p>Ready to explore a fresh alternative to Postman and Insomnia? Let&#x2019;s get started!</p><h2 id="what-is-httpyac">What is httpYac?</h2><p>httpYac is a lightweight, open-source tool for API testing and development. It combines a user-friendly interface with a powerful CLI, making it easy to send HTTP requests, test endpoints, and manage API collections.</p><p>One of the most powerful features of httpYac is its ability to define API requests as code, stored in simple text files. These files, often written in formats like <code>.http</code> or <code>.rest</code>, allow you to describe HTTP requests in a human-readable yet executable way. This approach not only makes it easy to version your API requests alongside your codebase using Git but also ensures that your API tests and documentation evolve with your application. By keeping your requests in sync with your code, you can improve collaboration, maintain consistency across environments, and simplify debugging.</p><p>You might be familiar with the <a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client&amp;ref=tuliocalil.com" rel="noreferrer"><strong>Rest Client</strong></a> extension, and yes, httpYac is quite similar in some ways. However, httpYac takes things to the next level with its powerful features. With httpYac, you can define <strong>environments</strong>, set up <strong>global variables</strong>, <strong>import files</strong>, use <strong>custom scripts</strong>, create <strong>tests</strong>, and much more. It&#x2019;s a versatile tool that adapts to your workflow, whether you&apos;re testing simple endpoints or managing complex API scenarios. Ready to see it in action? Let&#x2019;s dive in!</p><h2 id="starting">Starting</h2><p>httpYac offers both a <strong>CLI client</strong> and a <strong>VSCode extension</strong>, but for a fully integrated development experience, we&#x2019;ll focus on the extension. By using the VSCode extension, you can create a seamless, connected workspace directly within your codebase. Getting started is simple: just search for <strong>httpYac</strong> in the VSCode extensions menu and hit install.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="1691" height="592" srcset="https://tuliocalil.com/content/images/size/w600/2025/01/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/01/image.png 1000w, https://tuliocalil.com/content/images/size/w1600/2025/01/image.png 1600w, https://tuliocalil.com/content/images/2025/01/image.png 1691w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">This logo is so cool</span></figcaption></figure><p>Now we can create a new <code>.http</code> or <code>.rest</code> file and start write our collections, I create a folder structure for hold this:</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2025/01/image-1.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="385" height="212"></figure><p>Now, on the <code>login.http</code> file, lets write the first request, I will use <a href="https://dummyjson.com/docs?ref=tuliocalil.com" rel="noreferrer">DummyJSON</a> website for this post. The DummyJSON has a test endpoint, so, lets start from there.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-2.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="497" height="278"><figcaption><span style="white-space: pre-wrap;">Request example from the DummyJSON Doc.</span></figcaption></figure><p>So, write this request is very easy, just put the following code:</p><pre><code class="language-http">GET https://dummyjson.com/test</code></pre><p>Yes, is just this, but, we can see some new things on the VSCode:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-3.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="949" height="236" srcset="https://tuliocalil.com/content/images/size/w600/2025/01/image-3.png 600w, https://tuliocalil.com/content/images/2025/01/image-3.png 949w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Simple http request.</span></figcaption></figure><p>Upside the code, we can see some information&apos;s: </p><ul><li><code>send</code>: Action to run the request</li><li><code>env</code>: Select the enverimont, we will see this after.</li><li><code>session</code>: the stored user sessions(we can delete the sessions).</li></ul><p>Click in <code>send</code> and we can see the result:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-4.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="1701" height="395" srcset="https://tuliocalil.com/content/images/size/w600/2025/01/image-4.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/01/image-4.png 1000w, https://tuliocalil.com/content/images/size/w1600/2025/01/image-4.png 1600w, https://tuliocalil.com/content/images/2025/01/image-4.png 1701w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Request result.</span></figcaption></figure><p>When we send the request, we can see in the right panel the response, and we can see some status information, such as the status code, time (in ms), content-type, and the body.<br>To check more details, click on the &quot;header&quot; section, and we can see a detailed request log.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-5.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="1346" height="730" srcset="https://tuliocalil.com/content/images/size/w600/2025/01/image-5.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/01/image-5.png 1000w, https://tuliocalil.com/content/images/2025/01/image-5.png 1346w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Request log</span></figcaption></figure><p>I won&#x2019;t cover the basics here&#x2014;like HTTP methods or initial setup&#x2014;because <a href="https://httpyac.github.io/guide/request.html?ref=tuliocalil.com" rel="noreferrer">the official documentation</a> is straightforward and quick to read. Instead, let&#x2019;s skip ahead to the <strong>fun part</strong> and dive straight into what makes httpYac truly powerful and exciting to use!</p><h2 id="creating-the-login-request">Creating the login request</h2><p>Let&#x2019;s create the login request using the input, environment variables (<code>envs</code>), and global features to enhance our request. Here is the initial login request:</p><pre><code class="language-http">## Auth Login
# @title login
POST https://dummyjson.com/auth/login
Content-Type: application/json

{
    &quot;username&quot;: &quot;emilys&quot;,
    &quot;password&quot;: &quot;emilyspass&quot;
}</code></pre><p>Cool, but we can start using some cool features to boost our requests. First, let&apos;s create a <code>.httpyac.json</code> file. <a href="https://httpyac.github.io/config/?ref=tuliocalil.com" rel="noreferrer">This file is one of the files that <strong>httpYac</strong> uses to get environment values and other configurations</a>. Just create this file in your project root, alongside the <code>package.json</code>.</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2025/01/image-6.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="387" height="194"></figure><p>In this file we can create the <code>environments</code> key and create a key for each <code>ENV</code>that we have, including a <code>$shared</code>, that will share values between all envs.</p><pre><code class="language-json">{
  &quot;environments&quot;: {
    &quot;$shared&quot;: {},
    &quot;stage&quot;: {
      &quot;host&quot;: &quot;https://stage-dummyjson.com&quot;,
      &quot;username&quot;: &quot;emilys&quot;,
      &quot;password&quot;: &quot;emilyspass&quot;
    },
    &quot;dev&quot;: {
      &quot;host&quot;: &quot;https://dev-dummyjson.com&quot;,
      &quot;username&quot;: &quot;emilys&quot;,
      &quot;password&quot;: &quot;emilyspass&quot;
    },
    &quot;prod&quot;: {
      &quot;host&quot;: &quot;https://dummyjson.com&quot;,
      &quot;username&quot;: &quot;emilys&quot;,
      &quot;password&quot;: &quot;emilyspass&quot;
    }
  }
}
</code></pre><p>I created two variables:</p><ul><li><strong>host</strong>: The base URL for requests. <a href="https://httpyac.github.io/guide/variables.html?ref=tuliocalil.com#host" rel="noreferrer">&quot;host&quot; is a reserved variable name</a>. By setting the value here, we don&#x2019;t need to refer to it explicitly in the code. <strong>httpYac</strong> will automatically read this value and use it in all requests.</li><li><strong>username</strong>: The default user identification.</li><li><strong>password</strong>: The default user password.</li></ul><blockquote>You may need to restart VSCode for the extension to read the file. I&#x2019;m not sure if it&#x2019;s a VSCode bug or an extension issue, but it happens to me sometimes.</blockquote><blockquote>Don&#x2019;t forget to select the right environment in the menu at the top.</blockquote><p>I also created the <strong>stage</strong>, <strong>dev</strong>, and <strong>prod</strong> environments. This is a great idea in case you have different values for each environment.</p><p>Refactoring the login request, we got:</p><pre><code class="language-http">## Auth Login
# @title login
POST /auth/login
Content-Type: application/json

{
    &quot;username&quot;: &quot;{{username}}&quot;,
    &quot;password&quot;: &quot;{{password}}&quot;
}</code></pre><p>But, if we want do ask the password or the username?<br>Just change the value on <code>.httpyac.json</code> to use a <code>input</code> commando, this will show a input component in VSCode asking to user type something, we can use <code>input</code>, <code>password input</code> or <code>select</code> components. Lets refc and add the <code>input</code>:</p><p>But what if we want to ask for the password or the username?<br>Simply change the value in the <code>.httpyac.json</code> file to use an <code>input</code> command. This will display an input component in VSCode, prompting the user to type something. We can use <code>input</code>, <code>password input</code>, or <code>select</code> components. Let&apos;s refactor and add the <code>input</code>:</p><pre><code class="language-http">{
  &quot;environments&quot;: {
    &quot;$shared&quot;: {},
    &quot;stage&quot;: {
      &quot;host&quot;: &quot;https://stage-dummyjson.com&quot;,
      &quot;username&quot;: &quot;emilys&quot;,
      &quot;password&quot;: &quot;emilyspass&quot;
    },
    &quot;dev&quot;: {
      &quot;host&quot;: &quot;https://dev-dummyjson.com&quot;,
      &quot;username&quot;: &quot;emilys&quot;,
      &quot;password&quot;: &quot;emilyspass&quot;
    },
    &quot;prod&quot;: {
      &quot;host&quot;: &quot;https://dummyjson.com&quot;,
      &quot;username&quot;: &quot;{{$input Enter username $value: emilys}}&quot;,
      &quot;password&quot;: &quot;{{$input Enter password $value: emilyspass}}&quot;
    }
  }
}
</code></pre><blockquote>Very cool, we can create default values with <code>$value</code> prop and create a custom label.</blockquote><blockquote>We can use <code>$input-askonce</code> to store the input answer in a session and dont ask every-time we send a request.</blockquote><p>And when we send the VSCode ask to type the username and password:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-7.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="1102" height="485" srcset="https://tuliocalil.com/content/images/size/w600/2025/01/image-7.png 600w, https://tuliocalil.com/content/images/size/w1000/2025/01/image-7.png 1000w, https://tuliocalil.com/content/images/2025/01/image-7.png 1102w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">VSCode asking to type username</span></figcaption></figure><p>By checking the login response, we can see that it returns an <code>accessToken</code>, which is required to access subsequent endpoints (as expected). We don&apos;t want to copy and paste it into every request, so we can use the global object to store the returned token and reuse it in the following requests.</p><h2 id="global-variables-and-storing-the-response-token">Global variables and storing the response token</h2><p>Storing a value in the global state is very straightforward. We just need to access the <code>$global</code> object and create a new key with the desired value. Using this value later, after retrieving the response, is just as simple.</p><p>One of the <a href="https://httpyac.github.io/guide/scripting.html?ref=tuliocalil.com" rel="noreferrer">most powerful features of httpYac is the ability to use Node.js scripts</a> before or after requests. This allows us to read the response object, extract the token, and store it in the global state effortlessly.</p><pre><code class="language-http">## Auth Login
# @title login
POST /auth/login
Content-Type: application/json

{
    &quot;username&quot;: &quot;{{username}}&quot;,
    &quot;password&quot;: &quot;{{password}}&quot;
}

{{
    $global.accessToken = response.parsedBody.accessToken
}}</code></pre><p>Now we got the <code>accessToken</code> stored, lets create the next requests.</p><h2 id="request-on-authenticated-endpoint">Request on authenticated endpoint</h2><p>Authenticated endpoints can be a significant challenge for API testing and automation, but we have everything we need to overcome this.</p><p>Create a new folder and file for the <code>products</code> module:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-8.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="374" height="220"><figcaption><span style="white-space: pre-wrap;">Get all products request file</span></figcaption></figure><p>We already know how to setup our request end point and headers, we just need to create a new field: <code>Authorization</code> and read the value from <code>$global</code> object, and its is so easy:</p><pre><code class="language-http">GET /products
Authorization: Bearer {{$global.accessToken}}</code></pre><p>Yeah, its just that, we no need do anything else.</p><h2 id="finishing">Finishing</h2><p>httpYac is very simple and powerful, we can create very cool requests collections and keep it in the repository to use the GIT version control and team collaboration.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2025/01/image-9.png" class="kg-image" alt="Simplify API Testing and Collection Versioning Using httpYac" loading="lazy" width="358" height="303"><figcaption><span style="white-space: pre-wrap;">Other requests</span></figcaption></figure><p>I created other requests too, you can see this in the demo repository:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tuliocll/httpyac-post-demo?ref=tuliocalil.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - tuliocll/httpyac-post-demo: This is a demo for the post: Simplify API Testing and Collection Versioning Using httpYac</div><div class="kg-bookmark-description">This is a demo for the post: Simplify API Testing and Collection Versioning Using httpYac - tuliocll/httpyac-post-demo</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" alt="Simplify API Testing and Collection Versioning Using httpYac"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">tuliocll</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/191591519df9056ee9ea87b65cedc091a703f68476d31b67601e8576a4f55e9d/tuliocll/httpyac-post-demo" alt="Simplify API Testing and Collection Versioning Using httpYac"></div></a></figure><p>We can do a lot with that, I will create a part 2 showing how to integrate this with CI/CD, using the asserts and more. Subscribe to be notified!</p><div class="kg-card kg-signup-card kg-width-wide " data-lexical-signup-form style="background-color: #388a60; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #FFFFFF;"><span style="white-space: pre-wrap;">Sign up for Tulio Calil Dev</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #FFFFFF;"><span style="white-space: pre-wrap;">Building Quality Technological Solutions</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button " style="background-color: #467b3a;color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #FFFFFF;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #FFFFFF;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #FFFFFF;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Microlearning: Kubernetes Hacks - Simple Tips to Boost Your Workflow]]></title><description><![CDATA[<p>Kubernetes is a powerful tool, but navigating its complexities can sometimes feel overwhelming. The good news? A few practical hacks can make a big difference in how you manage and optimize your clusters. Whether it&#x2019;s seamless updates, efficient debugging, or smarter scaling, these tips are here to simplify</p>]]></description><link>https://tuliocalil.com/microlearn-kubernetes-hacks-simple-tips-to-boost-your-workflow/</link><guid isPermaLink="false">6752e0c422f399023d76da88</guid><category><![CDATA[Backend]]></category><category><![CDATA[Web]]></category><category><![CDATA[Performance]]></category><category><![CDATA[Boas práticas]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Fri, 06 Dec 2024 11:35:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1667372459510-55b5e2087cd0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDZ8fGt1YmVybmV0ZXN8ZW58MHx8fHwxNzMzNDg0NzI2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1667372459510-55b5e2087cd0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDZ8fGt1YmVybmV0ZXN8ZW58MHx8fHwxNzMzNDg0NzI2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Microlearning: Kubernetes Hacks - Simple Tips to Boost Your Workflow"><p>Kubernetes is a powerful tool, but navigating its complexities can sometimes feel overwhelming. The good news? A few practical hacks can make a big difference in how you manage and optimize your clusters. Whether it&#x2019;s seamless updates, efficient debugging, or smarter scaling, these tips are here to simplify your workflow and help you get the most out of Kubernetes. Let&#x2019;s dive in!</p><h3 id="update-configmaps-without-restarting-podsupdate-configmaps-without-restarting-pods">Update ConfigMaps Without Restarting PodsUpdate ConfigMaps Without Restarting Pods</h3><p>When your application relies on a ConfigMap, restarting Pods for every configuration change can be inefficient. Instead, mount the ConfigMap as a volume in your Pod. Updates to the ConfigMap will automatically reflect in the mounted volume.</p><pre><code class="language-yml">volumeMounts:
  - name: config-volume
    mountPath: /etc/config
volumes:
  - name: config-volume
    configMap:
      name: my-config
</code></pre>
<blockquote>Combine this approach with readiness probes to ensure the Pod is in a healthy state before serving traffic after updates.</blockquote><p>Another work around is adding a hash from the ConfigMap or Secret as an environment variable.</p><pre><code class="language-yml">env:
  - name: CONFIG_HASH
    valueFrom:
      configMapKeyRef:
        name: my-config
        key: hash
</code></pre>
<h3 id="inspect-pod-resource-usage-with-kubectl-topinspect-pod-resource-usage-with-kubectl-top">Inspect Pod Resource Usage with kubectl topInspect Pod Resource Usage with kubectl top</h3><p>Keep an eye on your cluster&#x2019;s health by checking CPU and memory usage for Pods and nodes with:Keep an eye on your cluster&#x2019;s health by checking CPU and memory usage for Pods and nodes with:</p><pre><code class="language-bash">kubectl top pod
</code></pre>
<blockquote>Enable the Metrics Server for this feature, and use it to detect bottlenecks and optimize resource allocation.</blockquote><h3 id="simplify-your-workflow-with-kubectl-aliasessimplify-your-workflow-with-kubectl-aliases">Simplify Your Workflow with kubectl aliasesSimplify Your Workflow with kubectl aliases</h3><p>Speed up your day-to-day tasks by creating aliases for common kubectl commands. Add these to your shell configuration file (.bashrc or .zshrc):Speed up your day-to-day tasks by creating aliases for common kubectl commands. Add these to your shell configuration file (.bashrc or .zshrc):</p><pre><code class="language-bash">alias k=&quot;kubectl&quot;
alias kgp=&quot;kubectl get pods&quot;
alias kdp=&quot;kubectl describe pod&quot;
</code></pre><blockquote>You can use/check a <a href="https://github.com/ahmetb/kubectl-aliases/blob/master/.kubectl_aliases?ref=tuliocalil.com" rel="noreferrer">really good alias list here</a>.</blockquote><h3 id="use-namespaces-to-separate-environmentsuse-namespaces-to-separate-environments">Use Namespaces to Separate EnvironmentsUse Namespaces to Separate Environments</h3><p>Namespaces allow you to logically separate resources within a cluster. For example, use dev, stage, and prod namespaces to isolate environments.</p><p>To list Pods in a specific namespace:Namespaces allow you to logically separate resources within a cluster. For example, use dev, stage, and prod namespaces to isolate environments.To list Pods in a specific namespace:</p><pre><code class="language-bash">kubectl get pods -n [namespace]
</code></pre><blockquote>Combine Namespaces with RBAC to grant fine-grained permissions and enhance security.</blockquote><h3 id="test-locally-with-kind-kubernetes-in-dockertest-locally-with-kind-kubernetes-in-docker">Test Locally with Kind (Kubernetes in Docker)Test Locally with Kind (Kubernetes in Docker)</h3><p><a href="https://kind.sigs.k8s.io/?ref=tuliocalil.com" rel="noreferrer">Kind</a> allows you to create Kubernetes clusters locally using Docker containers. It&apos;s a fast and lightweight way to test your deployments without requiring a cloud setup. Create a cluster with a simple command:Kind allows you to create Kubernetes clusters locally using Docker containers. It&apos;s a fast and lightweight way to test your deployments without requiring a cloud setup. Create a cluster with a simple command:</p><pre><code class="language-bash">kind create cluster
</code></pre><blockquote>Use Kind to simulate multi-node clusters or test specific Kubernetes versions before deploying to production. Perfect for quick experiments and development!</blockquote><div class="kg-card kg-signup-card kg-width-wide " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for Tulio Calil Dev</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Building Quality Technological Solutions</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div><p>Kubernetes offers endless possibilities to optimize and simplify your workflow, and these tips are just the beginning! Whether you&apos;re troubleshooting, scaling, or fine-tuning your cluster, there&apos;s always something new to learn. Have a favorite Kubernetes tip or hack that we didn&#x2019;t mention? Drop it in the comments&#x2014;we&#x2019;d love to hear how you&#x2019;re making the most out of Kubernetes!</p>]]></content:encoded></item><item><title><![CDATA[Microlearning: NestJS and JWE - How to use it]]></title><description><![CDATA[Learn how to secure data using JSON Web Encryption (JWE) in NestJS. This post compares JWE and JWT, focusing on data encryption and confidentiality. It provides a practical guide on setting up the node-jose library, managing keys, and implementing encryption and decryption services in NestJS. ]]></description><link>https://tuliocalil.com/nestjs-and-jwe-how-to-use-it/</link><guid isPermaLink="false">6655e232901bb50b9daa9323</guid><category><![CDATA[Backend]]></category><category><![CDATA[Nodejs]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Wed, 29 May 2024 13:32:02 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1517417191646-9f4cfa229f85?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDk5fHxjYXQlMjBsaWdodHxlbnwwfHx8fDE3MTY5MDQ2NjZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1517417191646-9f4cfa229f85?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDk5fHxjYXQlMjBsaWdodHxlbnwwfHx8fDE3MTY5MDQ2NjZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Microlearning: NestJS and JWE - How to use it"><p>JWE (JSON Web Encryption) is a compact, URL-safe means of representing encrypted content using JSON-based data structures. It is a standard for securing data by encrypting it, ensuring that only authorized parties can read the data. JWE is part of the larger suite of JSON Object Signing and Encryption (<a href="https://www.jamestharpe.com/jose/?ref=tuliocalil.com#:~:text=JSON%20Object%20Signing%20and%20Encryption%20(JOSE)%20is%20the%20set%20of,sign%20content%20as%20JSON%20data." rel="noreferrer">JOSE</a>) technologies, which also includes JSON Web Signature (JWS) and JSON Web Token (JWT).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/05/d8b40bce-4f50-414b-aea3-b82695284f8c.jpeg" class="kg-image" alt="Microlearning: NestJS and JWE - How to use it" loading="lazy" width="701" height="498" srcset="https://tuliocalil.com/content/images/size/w600/2024/05/d8b40bce-4f50-414b-aea3-b82695284f8c.jpeg 600w, https://tuliocalil.com/content/images/2024/05/d8b40bce-4f50-414b-aea3-b82695284f8c.jpeg 701w"><figcaption><span style="white-space: pre-wrap;">JSON Object Signing and Encryption (JOSE)&#xA0;Knowledge Graph</span></figcaption></figure><p>We can do a small and fast comparison between JWT (most common option)  and JWE:</p><table>
<thead>
<tr>
<th>Feature/Aspect</th>
<th>JWT (JSON Web Token)</th>
<th>JWE (JSON Web Encryption)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Purpose</strong></td>
<td>Authentication and information exchange</td>
<td>Data encryption to ensure confidentiality</td>
</tr>
<tr>
<td><strong>Structure</strong></td>
<td>Header, Payload, Signature</td>
<td>Protected Header, Encrypted Key, Initialization Vector (IV), Ciphertext, Authentication Tag</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>Data integrity and authenticity</td>
<td>Confidentiality, integrity, and authenticity</td>
</tr>
<tr>
<td><strong>Data Visibility</strong></td>
<td>Payload is base64-url encoded and signed, but not encrypted</td>
<td>Payload is encrypted and thus not readable without decryption</td>
</tr>
<tr>
<td><strong>Use Cases</strong></td>
<td>- Authentication tokens (e.g., OAuth)</td>
<td>- Encrypting sensitive data (e.g., personal or financial information)</td>
</tr>
<tr>
<td></td>
<td>- Information exchange where data needs to be verified</td>
<td>- Any scenario where data privacy and protection are crucial</td>
</tr>
<tr>
<td></td>
<td>- Stateless sessions</td>
<td></td>
</tr>
</tbody>
</table><p>To start working with JWE in NestJS (or any other Node.JS framework) we can use the <a href="https://www.npmjs.com/package/node-jose?ref=tuliocalil.com" rel="noreferrer"><code>node-jose</code></a> library. It&apos;s really simple to use, and this library implements (wherever possible) all algorithms, formats, and options in JWS, JWE and JWT, and uses native cryptographic support.</p><pre><code class="language-sh">yarn add node-jose
yarn add @types/node-jose -D</code></pre><p>Now we need to create a new service to hold our code:</p><pre><code class="language-sh">nest g service Encryption</code></pre><pre><code class="language-typescript">import { Injectable } from &apos;@nestjs/common&apos;;
import { JWE, JWK } from &apos;node-jose&apos;;
import { readFileSync } from &apos;fs&apos;;
import { join } from &apos;path&apos;;

@Injectable()
export class EncryptionService {
  // Create a new keystore, which will hold the symmetric( or asymmetric) key.
  private keystore: JWK.KeyStore;

  constructor() {
    // Initialize the keystore
    this.keystore = JWK.createKeyStore();

    // Load the symmetric key into the keystore
    this.loadKey();
  }

  /**
   * Reads the symmetric key from a file and adds it to the keystore.
   */
  async loadKey() {
    try {
      const key = await this.getSymmetricKey();

      await this.keystore.add(key);
    } catch (e) {
      console.log(&apos;Error adding key&apos;);
    }
  }

  /**
   * Reads the key from a file and returns it as an object,
   * which can be added to the keystore.
   *
   * @returns symmetric key as an object
   */
  async getSymmetricKey() {
    try {
      const path = join(process.cwd(), &apos;src/encryption/key.json&apos;);

      const key = readFileSync(path, &apos;utf8&apos;);

      return JSON.parse(key);
    } catch (e) {
      console.log(&apos;Error reading key file&apos;);
    }
  }

  /**
   * Encrypts data using the first key in the keystore,
   * which should be the symmetric key used for encryption,
   * and returns the encrypted data in JWE format.
   *
   * @param payload Data to encrypt
   * @returns encrypted data in JWE format (JSON Web Encryption) as string
   */
  async encrypt(payload: object) {
    // Get the first key in the keystore
    const key = this.keystore.all({ use: &apos;enc&apos; })[0];

    // Convert the payload to a string
    const input = JSON.stringify(payload);

    // Encrypt the payload using the key
    const encrypted = await JWE.createEncrypt({ format: &apos;compact&apos; }, key)
      .update(input)
      .final();

    // Return the encrypted data
    return encrypted;
  }

  /**
   * Decrypts the token using the keystore and returns the decrypted payload.
   * @param token Encrypted data in JWE format
   * @returns decrypted payload as an object
   */
  async decrypt(token: string) {
    const decrypted = await JWE.createDecrypt(this.keystore).decrypt(token);
    const payload = JSON.parse(decrypted.payload.toString());
    return payload;
  }
}
</code></pre><p>With this code we can: read a key in <code>json</code> format (used to encrypt and decrypt the data) encrypt and decrypt the payload.</p><blockquote>I put some comments in the code to explain each methods.</blockquote><div class="kg-card kg-signup-card kg-width-wide " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for Tulio Calil Dev</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Building Quality Technological Solutions</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div><p>My demo key (symmetric key):</p><pre><code class="language-json">{
  &quot;kty&quot;: &quot;oct&quot;,
  &quot;k&quot;: &quot;k1JnWRfC-5zzmL72vXIuBgTLfVROXBakS4OmGcrMCoc&quot;,
  &quot;alg&quot;: &quot;A256GCM&quot;,
  &quot;use&quot;: &quot;enc&quot;
}
</code></pre><p>Now we can create two routes to test the service:</p><pre><code class="language-typescript">  @Post(&apos;encrypt&apos;)
  async encrypt(@Body() payload: object) {
    return await this.encryptionService.encrypt(payload);
  }

  @Post(&apos;decrypt&apos;)
  async decryptData(@Body() payload: any): Promise&lt;object&gt; {
    return this.encryptionService.decrypt(payload.token);
  }</code></pre><p>We can test and see the encryption working:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/05/image.png" class="kg-image" alt="Microlearning: NestJS and JWE - How to use it" loading="lazy" width="1135" height="326" srcset="https://tuliocalil.com/content/images/size/w600/2024/05/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/05/image.png 1000w, https://tuliocalil.com/content/images/2024/05/image.png 1135w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">On the left is the payload to encrypt and on the right we get the encrypted token.</span></figcaption></figure><p>And the decryption:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/05/image-1.png" class="kg-image" alt="Microlearning: NestJS and JWE - How to use it" loading="lazy" width="1135" height="350" srcset="https://tuliocalil.com/content/images/size/w600/2024/05/image-1.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/05/image-1.png 1000w, https://tuliocalil.com/content/images/2024/05/image-1.png 1135w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">On the left, we get the encrypted token and on the right, the payload is decrypted.</span></figcaption></figure><p>This is a simple but very useful implementation of JWE on NestJS. Do you have any questions about this or what to say something about this new format of post? Let me know!</p><blockquote>This post is part of a serie called &quot;microlearning&quot;, <a href="#" rel="noreferrer">check more about what is that here</a>.</blockquote>]]></content:encoded></item><item><title><![CDATA[Migrating Native Android App to React Native in 2024]]></title><description><![CDATA[Upgrade your Android app to React Native in 2024 efficiently. This brief guide covers crucial steps and insights for a seamless transition, boosting cross-platform support and streamlining app maintenance. Ideal for developers seeking swift, scalable development solutions.]]></description><link>https://tuliocalil.com/migrating-native-android-app-to-react-native-in-2024/</link><guid isPermaLink="false">65ef7658901bb50b9daa8f5a</guid><category><![CDATA[React Native]]></category><category><![CDATA[Performance]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sun, 24 Mar 2024 21:56:43 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1598965402089-897ce52e8355?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEyfHxhbmRyb2lkfGVufDB8fHx8MTcxMDE5MjIyM3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1598965402089-897ce52e8355?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEyfHxhbmRyb2lkfGVufDB8fHx8MTcxMDE5MjIyM3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Migrating Native Android App to React Native in 2024"><p>In my career, I&apos;ve had to do various refactors and recreate applications, including Mobile, Frontend, and Backend, and I realized that I&apos;ve never documented this in a post. Recently, I was part of a project where we needed to migrate two native applications (Android in Kotlin and iOS in Swift) to React Native. In addition to migrating all the current features, the idea was to address all the technical debts from the previous versions (conscious debts that were not prioritized) and add new functionalities with a new interface.</p><p>I&apos;ll take this opportunity to talk about this process, but as an example, I&apos;ll use a very simple app that I developed a long time ago, the app in question is the &quot;<a href="https://play.google.com/store/apps/details?id=com.calango.com.lanternafrontaletraseiralite&amp;ref=tuliocalil.com" rel="noreferrer">Front Flashlight Lite</a>&quot; (yes, a flashlight app).<br>Google sent several alerts about SDK version updates and other policies, and the application even went offline because the terms of use were no longer online.</p><h2 id="overview">Overview</h2><p>The application operates in an extremely simple manner, we can:</p><ul><li>Turn the device&apos;s rear flashlight on and off (if available)</li><li>Turn the device&apos;s front flashlight on and off (if available)</li></ul><p>However, all this is done using the device&apos;s native camera API, which means I will need to create a native module for this in React Native.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://play-lh.googleusercontent.com/7Rx1FAHMch3hgwh-U2Gh6HBv9huNPqCnR6Ls8QdQK-MCQMCEanGKLUQw_5YqZHQS29w=w2560-h1440-rw" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Vers&#xE3;o atual da aplica&#xE7;&#xE3;o</span></figcaption></figure><p>The idea is not only to migrate to React Native but also to create a new interface and new icons to make everything more modern and attractive.<br><br>For this migration, we will focus only on Android, and in my planning, I will need to structure in React Native:</p><ul><li>React Native CLI to start a new project.</li><li>Configure the Bundle (package name) and use the same Keystore to replace the current app without having to create a new one.</li><li>Navigation with React Navigation.</li><li>Styling with Stylesheet (you don&apos;t have to use Styled components for everything)</li><li>React Native Animated (perhaps ReAnimated might deliver better performance)</li><li>Create a native module to access the camera API and turn on the flash.</li><li>Firebase library and Admob for analytics and advertisements</li></ul><h2 id="starting">Starting</h2><p>Let&apos;s start with the React Navigation part. Here, there&apos;s nothing out of the ordinary; we just need to add the libraries to the project and <a href="https://reactnavigation.org/docs/getting-started/?ref=tuliocalil.com" rel="noreferrer">follow the documentation</a> to ensure everything is correct.</p><pre><code class="language-bash">yarn add @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack</code></pre><p>After setting up the route (we have just one), we can now create our folder structure and then start on the UI.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="574" height="838"><figcaption><span style="white-space: pre-wrap;">Folder organization</span></figcaption></figure><p>After stylizing the home screen, we get this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-2.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="672" height="1398" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-2.png 600w, https://tuliocalil.com/content/images/2024/03/image-2.png 672w"><figcaption><span style="white-space: pre-wrap;">App with animation styles and animation</span></figcaption></figure><h2 id="native-module">Native module</h2><p>Now we need to write the native code, let&apos;s create a bridge between React Native and Android. I am writing about this right now and will post as soon as possible.<br>So, I created the Module and Package classes to control the camera API:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-3.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="514" height="210"><figcaption><span style="white-space: pre-wrap;">New java files in red</span></figcaption></figure><p>Register the Package in &quot;MainApplication&quot;:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-4.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="1594" height="480" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-4.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/03/image-4.png 1000w, https://tuliocalil.com/content/images/2024/03/image-4.png 1594w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Registering the Package</span></figcaption></figure><p>In React Native I created a <code>util</code> to communicate between RN and the Android code:</p><pre><code class="language-ts">import { NativeModules } from &apos;react-native&apos;;

export async function turnFlashlight(
  on: boolean,
  camera: 1 | 0,
): Promise&lt;boolean&gt; {
  const { LanternaManager } = NativeModules;

  try {
    return await new Promise((resolve, reject) =&gt; {
      LanternaManager.turn(on, camera, (status: boolean) =&gt; {
        if (status) {
          resolve(true);
        } else {
          reject(false);
        }
      });
    });
  } catch (e) {
    return false;
  }
}</code></pre><p>In the native code, the function has three arguments: </p><ul><li><code>status</code>: Turn on (<code>true</code>) or turn off(<code>false</code>) the light.</li><li><code>camera</code>: Which flash will be used; frontal (<code>1</code>) or backside (<code>0</code>).</li><li><code>callback</code>: Callback to capture the response passed in native code (<code>true</code> or <code>false</code>).</li></ul><h2 id="bundle-and-sign-the-application">Bundle and sign the application</h2><p>The most important part of the migration an application is here, all the text above is to illustrate a &quot;real migration case&quot;, but when you change your app, because you have to switch the framework, or create a new project from zero and need to replace the old application you need to be careful with two things:</p><ul><li>Application Id (or Bundle Id)</li><li>Keystore</li></ul><p>This is how the Play Store knows that this application is the same and not a new one (it&apos;s a simplified explanation), so, we create a new application and want to replace the old one, let&apos;s set up these two points, starting from the application ID changing.<br><strong>We don&apos;t need to change the package name, </strong>we need to change the <code>build.gradle</code> and don&apos;t need to refactor all the Android application references, folders, etc. <strong>It&apos;s very simple</strong>.</p><h3 id="application-id">Application ID</h3><p>Opening the <code>build.gradle</code> from <code>app</code> folder (<code>android/app/build.gradle</code>) we can look for the <code>defaultConfig</code> section. Here we can see the actual application ID.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-5.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="1140" height="580" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-5.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/03/image-5.png 1000w, https://tuliocalil.com/content/images/2024/03/image-5.png 1140w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Android configuration in build.gradle</span></figcaption></figure><p>You can check the actual application ID in the Play Console (or in the old codebase):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-7.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="1118" height="348" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-7.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/03/image-7.png 1000w, https://tuliocalil.com/content/images/2024/03/image-7.png 1118w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The application ID in Play Console</span></figcaption></figure><p>Don&apos;t forget to change the version number too, put the current + 1 (or some like that):</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2024/03/image-10.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="1346" height="394" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-10.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/03/image-10.png 1000w, https://tuliocalil.com/content/images/2024/03/image-10.png 1346w" sizes="(min-width: 720px) 720px"></figure><h3 id="keystore">KeyStore</h3><p>Now let&apos;s create a new signing config to register or old KeyStore, if you lost this, you can check <a href="https://support.google.com/googleplay/android-developer/thread/240359082?hl=en&amp;sjid=16294263128741993568-SA&amp;ref=tuliocalil.com" rel="noreferrer">some solutions to recovery</a>. <a href="https://reactnative.dev/docs/signed-apk-android?ref=tuliocalil.com" rel="noreferrer">The React Native documentation covers all these steps</a> very well.<br>In the same file, scroll to <code>signingConfigs</code> section and create a new entry:</p><pre><code>...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty(&apos;MYAPP_UPLOAD_STORE_FILE&apos;)) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            }
        }
    }
}
...</code></pre><p>Now in the <code>buildTypes</code> section, look for <code>release</code> and change the <code>signingConfig</code> to <code>signingConfigs.release</code> to use the newly created configs.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/03/image-12.png" class="kg-image" alt="Migrating Native Android App to React Native in 2024" loading="lazy" width="1808" height="438" srcset="https://tuliocalil.com/content/images/size/w600/2024/03/image-12.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/03/image-12.png 1000w, https://tuliocalil.com/content/images/size/w1600/2024/03/image-12.png 1600w, https://tuliocalil.com/content/images/2024/03/image-12.png 1808w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The changed signing config</span></figcaption></figure><p>Edit the <code>gradle.properties</code> in <code>android</code> folder to add the new variables and change the values. Put the same password and alias from the old app and put the <code>.keystore</code> file inside <code>android/app</code> folder.</p><blockquote>If your keystore is in <code>jks</code> format it&apos;s not a problem, just put the name with extension in the var.</blockquote><pre><code>MYAPP_UPLOAD_STORE_FILE=name-of-key-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****</code></pre><p>Now build and check:</p><pre><code class="language-bash">cd android
./gradlew bundleRelease # Generate .aab</code></pre><p>If everything is ok the Google Play Console will accept your new bundle and you are ready to go!</p><h2 id="bonus">Bonus</h2><p>Don&apos;t forget that if you are writing a new application is a good time to implement things to help you keep the good health of the code. Check these posts to see if this can help you:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://tuliocalil.com/react-native-ui-kit-for-2023/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">React Native UI Kit for 2023</div><div class="kg-bookmark-description">A list of react native ui kit and components libraries to boost your mobile development with beautiful components!</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://tuliocalil.com/favicon.ico" alt="Migrating Native Android App to React Native in 2024"><span class="kg-bookmark-author">Tulio Calil Dev</span><span class="kg-bookmark-publisher">Tulio Calil</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1581287053822-fd7bf4f4bfec?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHVpfGVufDB8fHx8MTY4OTM1ODUzOHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Migrating Native Android App to React Native in 2024"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://tuliocalil.com/how-to-boost-performance-in-react-applications/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to Boost Performance in React Applications</div><div class="kg-bookmark-description">React is a powerful library, but as applications become more complex, their performance can suffer. To optimize React application performance, see here some tips.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://tuliocalil.com/favicon.ico" alt="Migrating Native Android App to React Native in 2024"><span class="kg-bookmark-author">Tulio Calil Dev</span><span class="kg-bookmark-publisher">Tulio Calil</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1457364887197-9150188c107b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGxhdW5jaHxlbnwwfHx8fDE2NzY5MDQxNTQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Migrating Native Android App to React Native in 2024"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://tuliocalil.com/react-native-and-maestro/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">React Native and Maestro - UI Tests easy</div><div class="kg-bookmark-description">Maestro is a mobile UI testing framework, very simple and effective. It&#x2019;s an alternative to other frameworks like Appium, Espresso, UIAutomator or XCTest and is built on top of the knowledge from its predecessors. Maestro doesn&#x2019;t depend on any mobile framework, so you can run it in React Native app&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://tuliocalil.com/favicon.ico" alt="Migrating Native Android App to React Native in 2024"><span class="kg-bookmark-author">Tulio Calil Dev</span><span class="kg-bookmark-publisher">Tulio Calil</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1647093429566-13315b954173?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fG1hZXN0cm98ZW58MHx8fHwxNzA1Nzc2MzE3fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Migrating Native Android App to React Native in 2024"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://tuliocalil.com/optimizing-code-quality-in-react-and-react-native-with-sonarqube-integration/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Optimizing Code Quality in React and React Native with SonarQube Integration</div><div class="kg-bookmark-description">Optimize your React and React Native code quality with SonarQube integration. Explore efficient, scalable, and maintainable coding practices in this insightful guide.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://tuliocalil.com/favicon.ico" alt="Migrating Native Android App to React Native in 2024"><span class="kg-bookmark-author">Tulio Calil Dev</span><span class="kg-bookmark-publisher">Tulio Calil</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fHF1YWxpdHl8ZW58MHx8fHwxNzA1Njg2NDM2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Migrating Native Android App to React Native in 2024"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://dev.to/tuliocalil/aumente-as-conversoes-na-black-friday-estrategia-aso-icone-dinamico-no-react-native-3k81?ref=tuliocalil.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Aumente as Convers&#xF5;es na Black Friday: Estrat&#xE9;gia ASO &amp; &#xCD;cone Din&#xE2;mico no React Native</div><div class="kg-bookmark-description">Foto de CardMapr.nl na Unsplash Bem-vindo ao mundo da Black Friday, a &#xE9;poca do ano em que&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://media.dev.to/cdn-cgi/image/width=180,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png" alt="Migrating Native Android App to React Native in 2024"><span class="kg-bookmark-author">DEV Community</span><span class="kg-bookmark-publisher">Tulio Calil</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://media.dev.to/cdn-cgi/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2i3jvkz40ykpqkm47yg.png" alt="Migrating Native Android App to React Native in 2024"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[React Native and Maestro - UI Tests easy]]></title><description><![CDATA[<p><a href="https://maestro.mobile.dev/?ref=tuliocalil.com">Maestro</a> is a mobile UI testing framework, very simple and effective. It&apos;s an alternative to other frameworks like Appium, Espresso, UIAutomator or XCTest and is built on top of the knowledge from its predecessors.</p><p>Maestro doesn&apos;t depend on any mobile framework, so you can run it</p>]]></description><link>https://tuliocalil.com/react-native-and-maestro/</link><guid isPermaLink="false">65ac1483901bb50b9daa8d15</guid><category><![CDATA[React Native]]></category><category><![CDATA[Ferramentas]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sat, 20 Jan 2024 18:48:17 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1647093429566-13315b954173?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fG1hZXN0cm98ZW58MHx8fHwxNzA1Nzc2MzE3fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1647093429566-13315b954173?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fG1hZXN0cm98ZW58MHx8fHwxNzA1Nzc2MzE3fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="React Native and Maestro - UI Tests easy"><p><a href="https://maestro.mobile.dev/?ref=tuliocalil.com">Maestro</a> is a mobile UI testing framework, very simple and effective. It&apos;s an alternative to other frameworks like Appium, Espresso, UIAutomator or XCTest and is built on top of the knowledge from its predecessors.</p><p>Maestro doesn&apos;t depend on any mobile framework, so you can run it in React Native apps, Native Android or iOS, Flutter, Ionic, Native Script, etc.<br>It doesn&apos;t need a special app package too; you can use it even in a production/release build.</p><p>The best points of Maestro are:</p><ul><li>Simple setup.</li><li>Declarative syntax.</li><li>Fast iteration.</li><li>Built-in tolerance delay and flakiness.</li></ul><h2 id="installing">Installing</h2><p>The Maestro documentation is complete and covers installing steps for the specific OS; <a href="https://maestro.mobile.dev/getting-started/installing-maestro/macos?ref=tuliocalil.com">Macos</a>, <a href="https://maestro.mobile.dev/getting-started/installing-maestro/windows?ref=tuliocalil.com">Windows</a> and <a href="https://maestro.mobile.dev/getting-started/installing-maestro/linux?ref=tuliocalil.com">Linux</a>.</p><p>To install the Maestro CLI:</p><pre><code class="language-sh">curl -Ls &quot;https://get.maestro.mobile.dev&quot; | bash
</code></pre><blockquote>Don&apos;t forget to add the Maestro to <code>PATH</code> variable.</blockquote><p>Now we can check if the Maestro is successfully working:</p><pre><code class="language-sh">maestro -v
</code></pre><h2 id="creating-a-sample-flow">Creating a sample Flow</h2><p>Start your Android or iOS emulator/simulator, create a new folder for store our files, and open it in your favorite code editor:</p><p></p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpzd3843c6uvmkmxhlur.png" class="kg-image" alt="React Native and Maestro - UI Tests easy" loading="lazy"></figure><p>For this sample, let&apos;s write a simple flow to open the &quot;Maps&quot; app and search for a city, click to change the view from 2D to 3D, and change the map type to Satellite.</p><blockquote>Open the Maps before write/run this test, maybe you will be asked to give some permissions. I don&apos;t put this on the flow (but is possible) cause I want a clean example.</blockquote><p>In your editor or terminal create a new file <code>maps-flow.yml</code> and let&apos;s start to write the instructions.</p><p>In the first line, we got:</p><pre><code class="language-yml">appId: com.apple.Maps
</code></pre><blockquote>This <code>appId</code> is for iOS, if you are running android change it to <code>com.google.android.apps.maps</code></blockquote><p>This is the first line for every flow that we write, it tells Maestro what application we want to manipulate.<br>The first lines in the Maestro flow file are the configuration part, in fact, the configuration part is all the lines above the <code>---</code> marker(we will use this in the next part).<br>We can set the configuration in the flow file (like we see right now) or in a root <code>config.yml</code> file in the folder.</p><p>On the properties, we can for example run flow before the current in case we need any setup steps or somelike that.<br>You can check all the <a href="https://maestro.mobile.dev/api-reference/configuration/flow-configuration?ref=tuliocalil.com">properties here</a>.</p><p>So using the name and appId properties our config part looks like this:</p><pre><code class="language-yml">appId: com.apple.Maps
name: Maps Test
---
</code></pre><p>Here we use the marker <code>---</code> to tell the Maestro that above is the config and below is your workflow.</p><p>Now let&apos;s write the commands flow, before any command in the app we need to open the app, and for that, we use the <code>launchApp</code> command.</p><blockquote><code>Command</code> is the name of actions(the properties in the <code>YML</code>) that we got in Maestro.</blockquote><pre><code class="language-yml">appId: com.apple.Maps
name: Maps Test
---
  - launchApp
</code></pre><p>Note that have a space on the line of the command <code>launchApp</code>, this is how <code>YML</code> files work, we need indent to nesting. If it is not clear, you can <a href="https://www.redhat.com/en/topics/automation/what-is-yaml?ref=tuliocalil.com">read about YML</a> before starts.</p><p>The <code>launchApp</code> command <a href="https://maestro.mobile.dev/api-reference/commands/launchapp?ref=tuliocalil.com">accepts other params too</a>, you can launch another app, clear the state, give or not permissions, and more.<br>All the commands got a really good doc.</p><p>Now we can test the flow, in the terminal run:</p><pre><code class="language-sh">maestro test maps-flow.yml
</code></pre><blockquote>Sometimes it takes some time to install the maestro-drive in the device in the first run.</blockquote><p>If all is right we will see the following output:</p><p></p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xno3hc9j217xtsbkuopp.png" class="kg-image" alt="React Native and Maestro - UI Tests easy" loading="lazy"></figure><p>Let&apos;s put one more command:</p><pre><code class="language-yml">appId: com.apple.Maps
name: Maps Test
---
  - launchApp
  - tapOn: &quot;Search Maps&quot;
</code></pre><blockquote>&#x1F6A9; For Android emulator change the <code>tapOn</code> to &quot;Search here&quot;.</blockquote><p>The <code>tapOn</code> command tells the Maestro to tap/touch/click on a view/component/element that matches with the selector that we pass. By default we can pass a string as an argument, this means that Maestro will look for a view that has this EXACLTY text. It&apos;s very useful when you don&apos;t know or the application doesn&apos;t use things like <code>test-id</code>.<br>But like always we can pass any other option, in this case, other selectors.</p><p>In Maestro we got a lot of selectors, <code>text</code>, <code>id</code>, <code>point</code> (relative position in the screen), and more. <a href="https://maestro.mobile.dev/api-reference/selectors?ref=tuliocalil.com">All selectors are documented here</a>.<br>Right now the <code>text</code> selector is the best option for us.</p><p>We tap on the search field, and putting some text is the next step, right?</p><pre><code class="language-yml">appId: com.apple.Maps
name: Maps Test
---
  - launchApp
  - tapOn: &quot;Search Maps&quot;
  - inputText: &quot;Salvador, Bahia, Brazil&quot;
</code></pre><p>Now we type some text in the field, the <code>inputText</code> command has some random generators too, you can see it on the docs.<br>Now that we have typed the text let&apos;s confirm the search and seed the result:</p><pre><code class="language-yml">appId: com.apple.Maps # android = com.google.android.apps.maps 
name: Maps Test 
---
  - launchApp
  - tapOn: &quot;Search Maps&quot; # android = Search here
  - inputText: &quot;Salvador, Bahia, Brazil&quot;
  - pressKey: Enter
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sx4g303dh4oyulddctw5.png" class="kg-image" alt="React Native and Maestro - UI Tests easy" loading="lazy"></figure><p>If we want we can swipe the info modal, just add and test:</p><pre><code class="language-yml">  - swipe:
      start: 50%, 80%
      end: 50%, 10%
</code></pre><h2 id="using-parameters">Using parameters</h2><p>We are changing some values between OS, for iOS we using the <code>appId</code> &quot;com.apple.Maps&quot; and for Android &quot;com.google.android.apps.maps&quot;. In this case, we can turn this flow generic for each OS using parameters.</p><p>With parameters is possible to change the fixed values in the file for variables that will be read from the CLI arguments or from environment variables. Just a refactor in our file:</p><pre><code class="language-yml">appId: ${APP}
name: Maps Test 
---
  - launchApp
  - tapOn: ${FIELD}
  - inputText: &quot;Salvador, Bahia, Brazil&quot;
  - pressKey: Enter
  - swipe:
      start: 50%, 80%
      end: 50%, 10%
</code></pre><p>Now <code>appId</code> is the value of <code>APP</code> and <code>tapOn</code> is <code>FIELD</code>, to set this value just run the Maestro CLI this way:</p><p>For Android test:</p><pre><code class="language-sh">maestro test -e APP=com.google.android.apps.maps -e FIELD=&quot;Search here&quot;  maps-flow.yaml
</code></pre><p>For iOS test:</p><pre><code class="language-sh">maestro test -e APP=com.apple.Maps -e FIELD=&quot;Search Maps&quot;  maps-flow.yaml
</code></pre><p>We can use parameters for multiple things, if you have a secret (like a password) and don&apos;t want to store this in the <code>YML</code> file or if this value is dynamic and you need to get a new one every running for example.</p><h2 id="recording-and-report">Recording and report</h2><p>You may need to get some evidence that all tests are passing, maybe a video or a report or both. Maestro has a built-in solution for the two cases.</p><h3 id="recording">Recording</h3><p>Maestro records the mobile screen and creates a virtual window to show the test state and formats it to <code>mp4</code>, actually the rendering is done in the server and when the render is done you get a temporary URL to download the video.</p><p>To record the video is ultra simple, just change <code>test</code> to <code>record</code> in the CLI:</p><pre><code class="language-sh">maestro record -e APP=com.google.android.apps.maps -e FIELD=&quot;Search here&quot;  maps-flow.yaml
</code></pre><p>The output:<br></p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqfa0xt5jna6b0cc9h3w.gif" class="kg-image" alt="React Native and Maestro - UI Tests easy" loading="lazy"></figure><h3 id="report">Report</h3><p>Maestro can generate reports for test suites, in this case, we just got one test but we can generate the report too.<br>Like the record, the report is very simple too, just add a new argument in the CLI:</p><pre><code class="language-sh"> maestro test --format junit -e APP=com.google.android.apps.maps -e FIELD=&quot;Search Here&quot;  maps-flow.yaml
</code></pre><p>The <code>--format junit</code> will generate an <code>xml</code> file in the root with the status of the tests, actually the only supported format is Junit. You can change the output path using the <code>--output</code> argument.</p><p></p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xy14r1jfnl7qct4ikpm8.png" class="kg-image" alt="React Native and Maestro - UI Tests easy" loading="lazy"></figure><blockquote>Copy and paste the <code>xml</code> <a href="https://lotterfriends.github.io/online-junit-parser/?ref=tuliocalil.com#suite.0">content here</a></blockquote><h2 id="conclusion">Conclusion</h2><p>Maestro is an awesome framework and we use it a lot at my current company, <a href="https://www.gok.digital/?ref=tuliocalil">GOK</a>.</p><p>This is just the first post of a series about Maestro, I will bring real-world examples and advanced concepts in the next posts.</p>]]></content:encoded></item><item><title><![CDATA[Optimizing Code Quality in React and React Native with SonarQube Integration]]></title><description><![CDATA[Optimize your React and React Native code quality with SonarQube integration. Explore efficient, scalable, and maintainable coding practices in this insightful guide.]]></description><link>https://tuliocalil.com/optimizing-code-quality-in-react-and-react-native-with-sonarqube-integration/</link><guid isPermaLink="false">65aa929f901bb50b9daa8be0</guid><category><![CDATA[Boas práticas]]></category><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sat, 20 Jan 2024 01:33:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fHF1YWxpdHl8ZW58MHx8fHwxNzA1Njg2NDM2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fHF1YWxpdHl8ZW58MHx8fHwxNzA1Njg2NDM2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Optimizing Code Quality in React and React Native with SonarQube Integration"><p>In the dynamic world of software development, ensuring high code quality is crucial, especially for React and React Native frameworks. Code quality pertains to creating efficient, error-free, maintainable, and scalable code. SonarQube, a leading tool for code analysis, plays a pivotal role in achieving this. It automates code reviews, and identifies bugs, vulnerabilities, and code smells, making it an invaluable asset for developers. By integrating SonarQube with React and React Native projects, developers can significantly enhance their code&apos;s reliability, adhere to coding standards, and mitigate potential vulnerabilities, thereby maintaining a consistently high standard of code quality throughout the development process.</p><blockquote>Updated on October, 2025. Just some fixes on versions.</blockquote><p><strong>Requirements</strong></p><ul><li>Docker</li><li>Terminal</li><li>Code Editor</li></ul><p>Docker is the easiest and fastest way to run SonarQube in the project so I use it every time I need.</p><h2 id="starting">Starting</h2><p>On your React or React Native project create a <code>docker-compose.yml</code> file and put this:</p><pre><code class="language-yml">version: &quot;3.9&quot;

services:
  sonarqube:
    container_name: sonarqube
    image: sonarqube:8.9.10-community
    expose:
      - &quot;9000&quot;
    ports:
      - &quot;9000:9000&quot;
</code></pre><blockquote>If you are using an arm64 computer(like Macbook M1/M2/M?) change the image to <code>image: mwizner/sonarqube:8.7.1-community</code>.</blockquote><p>This will create a Docker container named &quot;sonarqube&quot; and expose the port <code>9000</code> to our machine.</p><p>In your terminal run: </p><pre><code class="language-bash">docker-compose up -d</code></pre><p>When it finishes, you can check if the container is running using the following command:</p><pre><code class="language-bash">docker ps</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-10.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="1194" height="384" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-10.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-10.png 1000w, https://tuliocalil.com/content/images/2024/01/image-10.png 1194w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Docker command output</span></figcaption></figure><p>Now you can open the localhost on the port <code>9000</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-11.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="1468" height="888" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-11.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-11.png 1000w, https://tuliocalil.com/content/images/2024/01/image-11.png 1468w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">SonarQube login page</span></figcaption></figure><p>The default user is <code>admin</code> and password <code>admin</code>. You will be asked to create a new (don&apos;t forget the new password). When you finish this page will open:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-12.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="2000" height="1398" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-12.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-12.png 1000w, https://tuliocalil.com/content/images/size/w1600/2024/01/image-12.png 1600w, https://tuliocalil.com/content/images/2024/01/image-12.png 2174w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">SonarQube dashboard</span></figcaption></figure><!--members-only--><p>Click on &quot;Create new project&quot; and choose the &quot;Manually&quot; option, now need to define the project key, it&apos;s just the project name (or whatever you want):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-13.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="1286" height="894" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-13.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-13.png 1000w, https://tuliocalil.com/content/images/2024/01/image-13.png 1286w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Project key</span></figcaption></figure><p>The next step is to generate a token, put a name for the token, click on &quot;Generate&quot;, copy the generated token:</p><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2024/01/image-14.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="1448" height="996" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-14.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-14.png 1000w, https://tuliocalil.com/content/images/2024/01/image-14.png 1448w" sizes="(min-width: 720px) 720px"></figure><p>Now we need to add a new dependency in the application:</p><pre><code class="language-bash">yarn add sonarqube-scanner@3^</code></pre><p>And create a file called <code>sonarscan.js</code> in the project root with this content:</p><pre><code class="language-js">const scanner = require(&quot;sonarqube-scanner&quot;);

scanner(
  {
    serverUrl: &quot;http://localhost:9000&quot;,
    login: &quot;the generated token&quot;,
    options: {
      &quot;sonar.projectName&quot;: &quot;project name&quot;,
      &quot;sonar.projectKey&quot;: &quot;project key&quot;,
      &quot;sonar.sources&quot;: &quot;src&quot;,
      &quot;sonar.ignore&quot;:
  &quot;src/**/*.test.js,src/**/*.spec.js,src/**/*.test.jsx,src/**/*.spec.jsx,android/**,ios/**,node_modules/**&quot;,
    },
  },
  () =&gt; process.exit()
);
</code></pre><p>Open the <code>package.json</code> and create a new script entry for the Sonar script:</p><pre><code class="language-json">&quot;scripts&quot;: {
    ...
    &quot;sonar&quot;: &quot;node sonarscan.js&quot;
  },</code></pre><p>Run in the terminal the new command:</p><pre><code class="language-bash">yarn sonar
#or
npm run sonar</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-15.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="1146" height="296" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-15.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-15.png 1000w, https://tuliocalil.com/content/images/2024/01/image-15.png 1146w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Sonar analytics output</span></figcaption></figure><blockquote>If you see an error message <code>Not authorized. Analyzing this project requires authentication.</code> Go to Sonar dashboard, open the <code>Administration</code> page, <code>Security</code> and disable the <code>Force user authentication</code> option.</blockquote><p>Go back to Sonar page and you will see some like this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-16.png" class="kg-image" alt="Optimizing Code Quality in React and React Native with SonarQube Integration" loading="lazy" width="2000" height="1120" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-16.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-16.png 1000w, https://tuliocalil.com/content/images/size/w1600/2024/01/image-16.png 1600w, https://tuliocalil.com/content/images/size/w2400/2024/01/image-16.png 2400w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">SonarQube project dashboard</span></figcaption></figure><p>Now you can navigate the issues and other insights generated by the Sonar!<br>Don&apos;t forget that you can create custom rules for Sonar.</p><p>Any question? Let me know in the comments section!</p>]]></content:encoded></item><item><title><![CDATA[PS2 SMB with a Raspberry Pi 4 in 2024]]></title><description><![CDATA[How to configure Raspberry Pi 4 to create a SAMBA server and access the games on your PlayStation 2!]]></description><link>https://tuliocalil.com/ps2-smb-raspberry-pi4-in-2024/</link><guid isPermaLink="false">6592ff7b901bb50b9daa89f5</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Tue, 02 Jan 2024 16:16:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1591196702597-062a87208fed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHBzMnxlbnwwfHx8fDE3MDQxMzI1MTJ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1591196702597-062a87208fed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHBzMnxlbnwwfHx8fDE3MDQxMzI1MTJ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="PS2 SMB with a Raspberry Pi 4 in 2024"><p>As a software engineer by profession, my world revolves around coding and creating innovative digital solutions. However, when I&apos;m not immersed in the complexities of software development, I find myself diving into another passion of mine: gaming. My love for video games is not just limited to the latest releases; in fact, I have a profound appreciation for vintage gaming consoles. My collection boasts treasures like the Super Nintendo Entertainment System (SNES), Nintendo 64, Game Boy, and several others. Each console is a gateway to nostalgia and a testament to the evolution of gaming. In this article, I&apos;m excited to share how I blend my technical skills with my gaming hobby, particularly through my recent project involving the PlayStation 2 (PS2) and a Raspberry Pi 4 in the year 2024 to create a Samba server to read the games over the network.</p><p>We will need:</p><ul><li>PlayStation 2 with <strong>OPL</strong></li><li>Raspberry Pi (I used <strong>4</strong>)</li><li>Ethernet cable (don&apos;t need to be crossover)</li><li>HDD (optional just to put all the games)</li></ul><h2 id="setup-raspberry-pi-4">Setup Raspberry Pi 4</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="2000" height="950" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image.png 1000w, https://tuliocalil.com/content/images/size/w1600/2024/01/image.png 1600w, https://tuliocalil.com/content/images/2024/01/image.png 2266w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Raspberry Pi Imager to create Raspberry Pi card</span></figcaption></figure><p>First of all, we need to format the SDCARD to use in our Raspberry, I recommend the Pi Imager to create the image. It&apos;s straightforward; choose the device, the operating system (I used and recommend the Raspberry Pi OS) and choose the storage (SDCARD).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-1.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1170" height="236" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-1.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-1.png 1000w, https://tuliocalil.com/content/images/2024/01/image-1.png 1170w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Raspberry Pi OS version</span></figcaption></figure><p>Now we can put the SDCARD in the Rasp and boot the system, finish all the initial setup.</p><h2 id="samba-server">Samba server</h2><p>Now we need to create our shared folder to access via OPL. Samba is an open-source software suite that provides seamless file and printer-sharing capabilities between Windows, Linux, and macOS systems within a network. It enables these diverse systems to communicate with each other by implementing the SMB/CIFS protocol.</p><p>Let&apos;s install the Samba, open the <strong>LXTerminal</strong>, and type:</p><pre><code class="language-bash">sudo apt install samba -y</code></pre><p>After finishing, we need to create or mount our shared folder where we will put all the games, we can use a USB device (HDD, pen drive, etc) or just use the SDCARD space. For this example, I will use an HDD with a USB adapter, just plug it and the OS will mount automatically.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-2.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1004" height="994" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-2.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-2.png 1000w, https://tuliocalil.com/content/images/2024/01/image-2.png 1004w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">HDD mounted on system</span></figcaption></figure><p>Now we need to get the path to the OPL folder in our device, just open the device that you plugged in and copy the path.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-3.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1436" height="308" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-3.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-3.png 1000w, https://tuliocalil.com/content/images/2024/01/image-3.png 1436w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">HDD path</span></figcaption></figure><p>If you will not use an external device, just create a folder and put the games <code>ISO</code> in the <code>DVD</code> folder.</p><p>Now let&apos;s configure the sharing file, you can install a code editor in your OS or just use <code>nano</code> to edit files in the terminal.<br>We need to open the <code>smb.conf</code> and edit some things, but is easier to create a new one, so let&apos;s move the original file and download a new one:</p><pre><code class="language-bash">sudo mv /etc/samba/smb.conf /etc/samba/smb.conf-bkp</code></pre><p>Now we will download the new file, I hosted in the <a href="https://gist.github.com/tuliocll/48e14b49148ce9bba22a345c8bb0d687?ref=tuliocalil.com" rel="noreferrer">GitHub gist</a>, it&apos;s a modification of an <a href="https://gist.github.com/mafredri/e88401c91489232e92e493d0e02912ef?ref=tuliocalil.com" rel="noreferrer">original file</a> with a small modification (<code>ntlm</code> line):</p><pre><code class="language-bash">sudo wget https://shorturl.at/dvQST -O /etc/samba/smb.conf</code></pre><p>This file has a lot of configs that make the SMB work on PS2,lets edit it:</p><pre><code class="language-bash">sudo nano /etc/samba/smb.conf</code></pre><p> In the bottom of the file, we got this:</p><pre><code class="language-conf ">[PS2]
comment = PlayStation 2
path = /media/user/DISK/PS2
browsable = yes
guest ok = yes
public = yes
available = yes
read only = no
veto files = /._*/.apdisk/.AppleDouble/.DS_Store/.TemporaryItems/.Trashes/desktop.ini/ehthumbs.db/Network Trash Folder/Temporary Items/Thumbs.db/
delete veto files = yes</code></pre><p>The comment is just a comment (seriously?), it&apos;s for the system that shows this info. The path is one of the most important lines here, we need to tell what folder we want to share, just paste the path that we copied before (HDD or the new folder). The other option is just for permissions. <br>Save the file (CTRL + o) and exit from <code>nano</code> (CTRL + x).</p><p>To finish our sharing server, we just need to config the user, we will use the current (logged) user to connect to the <code>SMB</code>, to do that we need &quot;create&quot; the user on the samba:</p><pre><code class="language-bash">sudo smbpasswd -a username</code></pre><p>Replace &quot;username&quot; to your current user. You will be asked to create a new password; you can create a different password for the sharing (simpler or smaller).<br>Now restart the services:</p><pre><code class="language-bash">sudo service smbd restart
sudo service nmdb restart</code></pre><h2 id="static-ip">Static IP</h2><p>To connect the PS2 to the Raspberry Pi with the cable we need to set a static IP to the ethernet network on Rasp, it&apos;s very easy on Raspberry Pi OS, just click on the WIFI icon &gt; Advanced Options &gt; Edit connections.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-4.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1252" height="626" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-4.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-4.png 1000w, https://tuliocalil.com/content/images/2024/01/image-4.png 1252w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Edit network connections</span></figcaption></figure><p>On the next screen choose the connection in &quot;Ethernet&quot; section and click on the gear icon:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-6.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1004" height="782" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-6.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-6.png 1000w, https://tuliocalil.com/content/images/2024/01/image-6.png 1004w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Edit ethernet configurations</span></figcaption></figure><p>Now click on IPv4 Tab and configure on this way:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-7.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="964" height="714" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-7.png 600w, https://tuliocalil.com/content/images/2024/01/image-7.png 964w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Setup IPv4</span></figcaption></figure><p>The network mask/subnet mask with &quot;24&quot; corresponds to &quot;255.255.255.0&quot;, <a href="https://unix.stackexchange.com/a/421981?ref=tuliocalil.com" rel="noreferrer">here is an explanation</a>.<br>Save the config, connect the cable on PS2 and Raspberry, and let&apos;s go to the OPL.</p><h2 id="opl-settings">OPL Settings</h2><p>Turn on your PS2 on OPL and go to Network Settings, put your configs like that:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2024/01/image-8.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy" width="1008" height="1246" srcset="https://tuliocalil.com/content/images/size/w600/2024/01/image-8.png 600w, https://tuliocalil.com/content/images/size/w1000/2024/01/image-8.png 1000w, https://tuliocalil.com/content/images/2024/01/image-8.png 1008w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">OPL network settings</span></figcaption></figure><p>On <strong>-PS2-</strong> section is our network config for the PlayStation 2, put the <strong>Address type</strong> to <strong>static</strong> and choose a different <strong>IP</strong>, the other info you will repeat from the Raspberry network configuration.<br>The <strong>- SMB Server -</strong> section is the config to connect to the Rasp, put the <strong>address type</strong> to <strong>IP</strong> and put the <strong>IP</strong> that we set previously, the <strong>port</strong> we don&apos;t need to change, the <strong>Share</strong> options is the name of share that we create on Samba config file (<strong>PS2</strong>), the <strong>user</strong> is the user that we use to log in on the Rasp, and the <strong>password</strong> is the one that we create when adding the user to Samba.</p><p>Now click on the &quot;<strong>Reconnect</strong>&quot; option and check if there&apos;s no error message, go back to your games list and check!</p><h2 id="bonus">Bonus</h2><p>You can use the OPL PC Tools to download the cover and background for your games and get a beautiful UI on your PS2!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://github.com/brainstream/OPL-PC-Tools/raw/master/screenshots/Main.png" class="kg-image" alt="PS2 SMB with a Raspberry Pi 4 in 2024" loading="lazy"><figcaption><span style="white-space: pre-wrap;">OPL Pc Tools preview</span></figcaption></figure><p><a href="https://github.com/brainstream/OPL-PC-Tools?ref=tuliocalil.com" rel="noreferrer">Check it out here.</a></p><p>Any question? comment and let&apos;s talk!</p>]]></content:encoded></item><item><title><![CDATA[My environment and tools for 2024 as a DEV]]></title><description><![CDATA[Discover my handpicked 2024 development toolkit—a blend of essential programs, tools, and plugins that drive my productivity and innovation as a developer. Explore how these resources equip me to thrive in the ever-evolving world of software development]]></description><link>https://tuliocalil.com/my-current-enveriment-and-tools-for-2024-as-a-dev/</link><guid isPermaLink="false">652295e0b83f2e1c295afe9e</guid><category><![CDATA[Ferramentas]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Wed, 11 Oct 2023 14:56:16 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1597484661643-2f5fef640dd1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fHRvb2xzfGVufDB8fHx8MTY5NjgwMjQ4Mnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1597484661643-2f5fef640dd1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fHRvb2xzfGVufDB8fHx8MTY5NjgwMjQ4Mnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="My environment and tools for 2024 as a DEV"><p>As the world of software development continues to evolve at a breakneck pace, so too does the toolkit of a modern developer. As we step into the year 2024, I find myself excited to share with you an overview of my current development environment and the essential tools that have become integral to my daily workflow. In this post, I&apos;ll walk you through the technologies, languages, frameworks, and hardware that empower me as a developer in this dynamic era of software engineering.</p><p>In a field where change is the only constant, staying up-to-date with the latest trends and tools is not just an option, but a necessity. Join me on this journey as we explore the key components that make up my development setup in 2024, and how they enable me to build, innovate, and thrive in the ever-evolving world of software development.</p><h2 id="code-editoride">Code editor/IDE</h2><p>In the world of software development, two essential tools take center stage: code editors and Integrated Development Environments (IDEs). These are the workspaces where developers bring their ideas to life, write, and refine code. While they share the common goal of facilitating code creation, they each have their own unique role and advantages.</p><h3 id="zed"><a href="https://zed.dev/?ref=tuliocalil.com" rel="noreferrer">Zed</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-6.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1538" height="1516" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-6.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-6.png 1000w, https://tuliocalil.com/content/images/2023/10/image-6.png 1538w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Zed code editor</span></figcaption></figure><p>Write in <a href="https://www.rust-lang.org/pt-BR?ref=tuliocalil.com" rel="noreferrer">Rust</a>, Zed is awesome and I fell in love with this editor, it is fast, simple, and has a <a href="https://zed.dev/features?ref=tuliocalil.com" rel="noreferrer">lot of features</a>. Unfortunately, it has only a Mac version, but you can see the <a href="https://github.com/zed-industries/community/issues/174?ref=tuliocalil.com" rel="noreferrer">other system version&apos;s status here</a>, I miss some features yet, I come from Visual Studio Code and I like things like mini-map, Git integrated client (I use the merge/diff a lot), and the possibility to choose icons library. All these features are listed as &quot;Top issues&quot; in the Zed repository and maybe it&apos;s come real in the feature.</p><h3 id="visual-studio-code"><a href="https://code.visualstudio.com/?ref=tuliocalil.com" rel="noreferrer">Visual Studio Code</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-7.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1582" height="1318" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-7.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-7.png 1000w, https://tuliocalil.com/content/images/2023/10/image-7.png 1582w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Visual Studio Code</span></figcaption></figure><p>VSCode is still the most complete code editor, in my opinion, writing in <a href="https://tuliocalil.com/tag/electron/" rel="noreferrer">Electron</a>, this code editor is my secure place to develop. Yes, I use Zed, but when I need things that I don&apos;t see there I switch to VSCode again.<br>Here are my settings and plugins in VSCode:</p><ul><li><a href="https://marketplace.visualstudio.com/items?itemName=teabyii.ayu&amp;ref=tuliocalil.com" rel="noreferrer">Ayu theme</a></li><li><a href="https://github.com/microsoft/cascadia-code?ref=tuliocalil.com" rel="noreferrer">Cascadia Code font</a></li><li><a href="https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight&amp;ref=tuliocalil.com" rel="noreferrer">Color highlight</a></li><li><a href="https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv&amp;ref=tuliocalil.com" rel="noreferrer">DotENV</a></li><li><a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig&amp;ref=tuliocalil.com" rel="noreferrer">EditorConfig</a></li><li>Eslint</li><li>File-icons</li><li>Github Copilot</li><li>gitignore</li><li>GitLens</li><li>Handlebars</li><li>HTML Preview</li><li>Live Server</li><li>Live Share</li><li>Path Intellisense</li><li>Prettier</li><li>TODO Tree</li><li>vscode-styled-components</li><li>WakaTime</li></ul><h2 id="terminal">Terminal</h2><p>A terminal emulator is a thing that I always try some new and always back to the same: Macos: iTerm2, Linux: Tilix, Windows: Windows terminal.<br>In this case, I use <a href="https://iterm2.com/?ref=tuliocalil.com" rel="noreferrer">iTerm2</a>, it&apos;s simple and has a lot of features that I like; split panel, tabs, profiles, and themes.</p><h3 id="iterm2">iTerm2</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-8.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1154" height="838" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-8.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-8.png 1000w, https://tuliocalil.com/content/images/2023/10/image-8.png 1154w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">iTerm2</span></figcaption></figure><p>For the theme I choose the <a href="https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/BlulocoDark.itermcolors?ref=tuliocalil.com" rel="noreferrer">Bluloco Dark</a> but you can <a href="https://iterm2colorschemes.com/?ref=tuliocalil.com" rel="noreferrer">find more here</a>.</p><h3 id="fish-shell"><a href="https://fishshell.com/?ref=tuliocalil.com" rel="noreferrer">Fish Shell</a></h3><p>This is another piece of my environment that I like to try new, for a long time I used <a href="https://ohmyz.sh/?ref=tuliocalil.com" rel="noreferrer">ZSH</a>, but a friend showed me fish one day and I decided to try, that was a year ago. Fish has a built-in autocomplete and many other features.</p><blockquote>Fish is considered an exotic shell since it does not adhere to POSIX shell standards - <a href="https://en.wikipedia.org/wiki/Fish_(Unix_shell)?ref=tuliocalil.com" rel="noreferrer">Wikipedia</a></blockquote><p>Check this gist to see <a href="https://gist.github.com/tuliocll/916a75a259a2bedffe27a1a919a72225?ref=tuliocalil.com" rel="noreferrer">my alias and configs in fish</a>, my plugins:</p><h4 id="fisher"><a href="https://github.com/jorgebucaran/fisher?ref=tuliocalil.com" rel="noreferrer">Fisher</a></h4><p>A plugin manager for Fish.</p><h4 id="peco"><a href="https://github.com/takashabe/fish-peco?ref=tuliocalil.com" rel="noreferrer">Peco</a></h4><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2023/10/image-9.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1144" height="826" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-9.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-9.png 1000w, https://tuliocalil.com/content/images/2023/10/image-9.png 1144w" sizes="(min-width: 720px) 720px"></figure><p>Peco is a filtering tool to use with other commands like <code>history</code>, <code>top</code> or many others.</p><h4 id="exa"><a href="https://the.exa.website/?ref=tuliocalil.com" rel="noreferrer">Exa</a></h4><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2023/10/image-10.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1150" height="840" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-10.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-10.png 1000w, https://tuliocalil.com/content/images/2023/10/image-10.png 1150w" sizes="(min-width: 720px) 720px"></figure><p>Exa is an alternative to <code>ls</code> command, it adds icons to the files, change the view of files, you can toggle the list columns, filter, and much more.</p><h4 id="z"><a href="https://github.com/jethrokuan/z?ref=tuliocalil.com" rel="noreferrer">Z</a></h4><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-11.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1148" height="836" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-11.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-11.png 1000w, https://tuliocalil.com/content/images/2023/10/image-11.png 1148w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Z directory jumping</span></figcaption></figure><p>Z is a directory jumping, Z tracks the directories you visit. With a combination of frequency and recency, it enables you to jump to the directory in mind.</p><h4 id="starship"><a href="https://starship.rs/?ref=tuliocalil.com" rel="noreferrer">Starship</a></h4><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-12.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1610" height="912" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-12.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-12.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image-12.png 1600w, https://tuliocalil.com/content/images/2023/10/image-12.png 1610w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Cross shell prompt</span></figcaption></figure><p>Starship is a customizable prompt that can be used in any shell.</p><h2 id="resthttp-request-client">Rest/HTTP request client</h2><p>REST clients are the go-to tools for developers and engineers when it comes to testing, debugging, and consuming RESTful APIs. They provide a user-friendly interface for crafting, sending, and receiving HTTP requests, making the exploration and interaction with web services both efficient and accessible.</p><h3 id="httpie"><a href="https://httpie.io/?ref=tuliocalil.com" rel="noreferrer">HTTPie</a></h3><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2023/10/image-13.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="1682" height="1116" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-13.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-13.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image-13.png 1600w, https://tuliocalil.com/content/images/2023/10/image-13.png 1682w" sizes="(min-width: 720px) 720px"></figure><p>HTTPie is a new software in this area but it looks awesome, the UI is very simple and beautiful, and they have clients for all the systems including a <a href="https://httpie.io/cli?ref=tuliocalil.com" rel="noreferrer">CLI</a> and a <a href="https://httpie.io/app?ref=tuliocalil.com" rel="noreferrer">Web-based client</a>.</p><h2 id="databases">Databases</h2><p>In software development, databases serve as a reliable storage and retrieval system, enabling applications to store user profiles, process transactions, track inventory, and much more. Developers leverage various types of databases, such as relational databases like MySQL and PostgreSQL, NoSQL databases like MongoDB, and in-memory databases like Redis, to address specific project requirements. <br>I have many tools for each database that I use, so this is the list:</p><ul><li><a href="https://www.mongodb.com/products/tools/compass?ref=tuliocalil.com" rel="noreferrer">MongoDB Compass</a></li><li><a href="https://github.com/Paxa/postbird?ref=tuliocalil.com" rel="noreferrer">Postgress Postbird</a></li><li><a href="https://sqlitebrowser.org/?ref=tuliocalil.com" rel="noreferrer">DB Browser for SQLite</a></li></ul><h2 id="debuggers">Debuggers</h2><p>Debugging tools are indispensable assets in the world of software development. They are designed to uncover and resolve issues, glitches, and errors within code, ensuring that applications run smoothly and efficiently. Debugging is a critical phase in the development process, allowing developers to identify and fix issues that may disrupt a program&apos;s functionality.</p><h3 id="flipper"><a href="https://fbflipper.com/?ref=tuliocalil.com" rel="noreferrer">Flipper</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://fbflipper.com/img/inspector.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Flipper</span></figcaption></figure><p>Flipper is a platform for debugging iOS, Android, and React Native apps. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API.</p><h3 id="react-native-debugger"><a href="https://github.com/jhen0409/react-native-debugger?ref=tuliocalil.com" rel="noreferrer">React Native Debugger</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://user-images.githubusercontent.com/3001525/29451479-6621bf1a-83c8-11e7-8ebb-b4e98b1af91c.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy"><figcaption><span style="white-space: pre-wrap;">React Native Debugger</span></figcaption></figure><p>A great and complete alternative to browser dev tools. You can interact with the components doom, see actions, logs, requests and more.</p><h3 id="reactotron"><a href="https://github.com/infinitered/reactotron?ref=tuliocalil.com" rel="noreferrer">Reactotron</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-14.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy" width="998" height="1180" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-14.png 600w, https://tuliocalil.com/content/images/2023/10/image-14.png 998w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Reactotron</span></figcaption></figure><p>An inspector for React and React Native, view requests &amp; responses, logs, state, and much more.</p><h3 id="debugtron"><a href="https://github.com/pd4d10/debugtron?ref=tuliocalil.com" rel="noreferrer">Debugtron</a></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://github.com/pd4d10/debugtron/raw/main/assets/0.png" class="kg-image" alt="My environment and tools for 2024 as a DEV" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Debugtron</span></figcaption></figure><p>An Electron debugger tool to debug production applications.</p><h2 id="others">Others</h2><p>So, I use many other things like software for notes, to draw diagrams, to free space in the Mac, and more, here is the list that I see right now:</p><ul><li><a href="https://www.npmjs.com/package/npkill?ref=tuliocalil.com" rel="noreferrer">NPKill</a></li><li><a href="https://cleanmymac.com/pt?ref=tuliocalil.com" rel="noreferrer">Clean my mac</a></li><li><a href="https://www.microsoft.com/pt-br/microsoft-365/microsoft-to-do-list-app?ref=tuliocalil.com" rel="noreferrer">Microsoft TO DO</a></li><li><a href="https://www.notion.so/pt-br?ref=tuliocalil.com" rel="noreferrer">Notion</a></li><li><a href="https://app.diagrams.net/?ref=tuliocalil.com" rel="noreferrer">Draw.io</a></li><li><a href="https://bitwarden.com/?ref=tuliocalil.com" rel="noreferrer">Bitwarden</a></li></ul><p>Crafting a powerful development environment for 2024 has been a key focus. The tools and plugins I&apos;ve highlighted in this post are the bedrock of my productivity and efficiency as a developer. With these resources at my disposal, I&apos;m poised to tackle the challenges and opportunities of the ever-evolving tech landscape.</p><div class="kg-card kg-product-card">
            <div class="kg-product-card-container">
                <img src="https://tuliocalil.com/content/images/2023/10/51J-Z3wDJkL.jpg" width="389" height="500" class="kg-product-card-image" loading="lazy" alt="My environment and tools for 2024 as a DEV">
                <div class="kg-product-card-title-container">
                    <h4 class="kg-product-card-title"><span style="white-space: pre-wrap;">The Robert C. Martin Clean Code Collection (Collection)</span></h4>
                </div>
                
                    <div class="kg-product-card-rating">
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class=" kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                    </div>
                

                <div class="kg-product-card-description"><p><span style="white-space: pre-wrap;">from: </span><i><em class="italic" style="white-space: pre-wrap;">$60.00</em></i><span style="white-space: pre-wrap;"> now: </span><b><strong style="white-space: pre-wrap;">$43.99</strong></b></p></div>
                
                    <a href="https://amzn.to/3RRFnI2?ref=tuliocalil.com" class="kg-product-card-button kg-product-card-btn-accent" target="_blank" rel="noopener noreferrer"><span>SAVE NOW</span></a>
                
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT]]></title><description><![CDATA[Unlock the potential of Node.js and Fastify as we delve into building a high-performance REST API. But that's not all – we're taking it a step further by seamlessly integrating OpenAI's Chat GPT.]]></description><link>https://tuliocalil.com/unleash-the-power-of-node-js-and-fastify-build-a-rest-api-and-connect-it-to-open-ai-chat-gpt/</link><guid isPermaLink="false">65122d39b83f2e1c295afc8e</guid><category><![CDATA[Backend]]></category><category><![CDATA[Nodejs]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sat, 07 Oct 2023 23:33:10 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1686191129324-91a5658b4a98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDM1fHxBSXxlbnwwfHx8fDE2OTU2OTAxMTZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1686191129324-91a5658b4a98?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDM1fHxBSXxlbnwwfHx8fDE2OTU2OTAxMTZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT"><p>In the ever-evolving landscape of web development, Node.js and Fastify stand as formidable pillars of efficiency and performance. Node.js, known for its speed and versatility, and <a href="https://fastify.dev/?ref=tuliocalil.com" rel="noreferrer">Fastify</a>, recognized for its lightning-fast web framework, together form a dynamic duo for building robust REST APIs. But what if we told you that there&apos;s a way to take your API to the next level, infusing it with the capabilities of OpenAI&apos;s Chat GPT?</p><p>In this comprehensive guide, we embark on a journey through the realms of Node.js, Fastify, and OpenAI&apos;s Chat GPT, unlocking their full potential to create an API experience like no other.</p><p><strong>Node.js: Fueling the Backend Revolution</strong><br>Node.js has been a game-changer in backend development, enabling developers to use JavaScript on the server-side. Its non-blocking, event-driven architecture makes it a natural choice for building highly scalable applications. We&apos;ll dive deep into how Node.js empowers our project with speed, efficiency, and an extensive library ecosystem.</p><p><strong>Fastify: The Swift and Secure Web Framework</strong><br>Fastify, a web framework for Node.js, is designed for blazing-fast performance. It excels in handling high loads while maintaining a minimalistic and developer-friendly API. We&apos;ll explore how Fastify&apos;s features and plugins streamline the creation of our RESTful API, ensuring it&apos;s both performant and secure.</p><p><strong>OpenAI Chat GPT: The AI Language Model Revolution</strong><br>OpenAI&apos;s Chat GPT represents a breakthrough in natural language understanding. With its ability to generate human-like text, it opens up exciting possibilities for conversational interfaces and content generation. We&apos;ll integrate Chat GPT seamlessly into our API, creating a dynamic and responsive user experience.</p><h2 id="creating-the-project">Creating the project</h2><p>To start the project, we need to create a new Node.js project and install Fastify to our project dependencies, create a new folder, and:</p><pre><code class="language-bash">npm init -y
#or
yarn init -y</code></pre><p>Now install the Fastify dependency:</p><pre><code class="language-bash">npm install fastify
#or
yarn add fastify</code></pre><p>Add <code>dotenv</code> too:</p><pre><code class="language-javascript">npm install dotenv
#or
yarn add dotenv</code></pre><p>Open the project in your code editor:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="2000" height="1506" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image.png 1600w, https://tuliocalil.com/content/images/2023/10/image.png 2002w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Project in ZED code editor.</span></figcaption></figure><p>It&apos;s time to create the folder structure and some files to get our project running, I like this structure:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-1.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="580" height="536"><figcaption><span style="white-space: pre-wrap;">Folders and files</span></figcaption></figure><p>In your <code>package.json</code> create an entry for <code>&quot;type&quot;: &quot;module&quot;</code>, this will allow the project to use <code>import/export</code> syntax. In <code>main</code> entry, change to <code>server.js.</code></p><p>Let&apos;s create our server module, open <code>app.js</code> and write a code that imports the routes file and setup the Fastify:</p><pre><code class="language-javascript">import Fastify from &apos;fastify&apos;;
import Routes from &apos;./routes.js&apos;;

const fastify = Fastify({
  logger: true,
});

Routes({ fastify });

export default fastify;
</code></pre><p>In the <a href="https://fastify.dev/docs/latest/Reference/Server?ref=tuliocalil.com#factory" rel="noreferrer">Fastify options</a> we will enable only the <a href="https://fastify.dev/docs/latest/Reference/Logging?ref=tuliocalil.com" rel="noreferrer">logger</a>.</p><p>In the <code>routes.js</code> temporarily we can register a new route and redirect to the controller:</p><pre><code class="language-javascript">import * as GPTController from &apos;./controllers/GPTController.js&apos;;

const gptRoutes = (fastify, _, done) =&gt; {
  fastify.get(&apos;/&apos;, GPTController.search);
  done();
}

export default async function Routes({ fastify }) {
  fastify.register(gptRoutes, { prefix: &apos;v1/gpt&apos; });
}
</code></pre><p>This code imports the <code>GPTController</code> (we will create it) and use the <code>search</code> method on <code>fastify.get</code>, this line means that when the user hits the <code>.../gpt</code> using the <code>GET</code> HTTP verb we will call the <code>search</code> function inside the controller.</p><p>We got the <code>Routes</code> function too, this function just registers the routes on Fastify and permits the creation of route prefixes (and other settings), this is very useful for grouping the resources by context or creating versions for each route group.</p><p>Open the <code>server.js</code> file and let&apos;s see the code:</p><pre><code class="language-javascript">import &apos;dotenv/config&apos;
import fastify from &apos;./src/app.js&apos;;

try {
  const port = parseInt(process.env.PORT, 10) || 7777;
  await fastify.listen({ port });
} catch (err) {
  throw err
}
</code></pre><p>Here we just import <code>dotenv</code> and execute the config setup, import the <code>app.js</code> file, and start the Fastify server with <code>listen</code> a function passing the port as a parameter. We get the port number from the <code>Env</code>.</p><p>Create the controller file inside <code>controllers</code> folder with name <code>GPTController.js</code> and put a small &quot;Hello World&quot;:</p><pre><code class="language-javascript">export const search = (request) =&gt; {
  return {message: &quot;Hello World&quot;}
}
</code></pre><p>Just a small function that returns an object with <code>message</code> key and as a value; &quot;Hello World&quot;.<br>Fastify automatically serializes the return value of the controller to a JSON and we can speed up this process (yes, serializing is slow) using the <code>response</code> key on the <code>schema</code> option for each route, <a href="https://fastify.dev/docs/latest/Reference/Validation-and-Serialization?ref=tuliocalil.com#serialization" rel="noreferrer">check it here</a>. It is not necessary but is good to know.</p><p>Time to run the server and see all the changes that we made:</p><pre><code class="language-bash">node server.js</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-2.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="1150" height="846" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-2.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-2.png 1000w, https://tuliocalil.com/content/images/2023/10/image-2.png 1150w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Fastify server running</span></figcaption></figure><p>Some logs will be printed on the terminal, this is because we enable the logger, and every request will output some logs too.</p><p>Open the server on an HTTP client (I use <a href="https://httpie.io/?ref=tuliocalil.com" rel="noreferrer">HTTPie</a>) with the new route and we can see the &quot;Hello World&quot; message:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-3.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="1676" height="1106" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-3.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-3.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image-3.png 1600w, https://tuliocalil.com/content/images/2023/10/image-3.png 1676w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Server response for GPT route</span></figcaption></figure><p>Okay, the main application is done! Go to check the OpenAI GPT part.</p><!--members-only--><h2 id="chat-gpt-on-nodejs">Chat GPT on Node.js</h2><p>From a technical standpoint, the Chat GPT API offers a seamless integration process, making it accessible and adaptable for a wide range of development projects. Developers can interact with the API via HTTP requests, allowing for straightforward communication with the model. Input is provided as a list of messages, each having a role (&apos;system,&apos; &apos;user,&apos; or &apos;assistant&apos;) and content. This structure enables the creation of structured and dynamic conversations, with the model able to remember and respond contextually to previous messages. The API supports customization through &apos;system&apos; level instructions, enabling developers to steer the model&apos;s behavior to align with specific use cases. With OpenAI&apos;s commitment to continuous improvement, the Chat GPT API promises not only an impressive initial experience but also the potential for further refinement as the model evolves, ensuring that it remains at the forefront of cutting-edge AI technology.</p><p>To connect to Node.js applications we can use the <a href="https://www.npmjs.com/package/openai?ref=tuliocalil.com" rel="noreferrer">openai</a> NPM package, this will give us a shortcut in development and simplify the application.<br>To start we need first to get an API Key, which you can get on the <a href="https://platform.openai.com/account/api-keys?ref=tuliocalil.com" rel="noreferrer">API key page</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-4.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="1706" height="706" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-4.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-4.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image-4.png 1600w, https://tuliocalil.com/content/images/2023/10/image-4.png 1706w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">API Key list</span></figcaption></figure><p>On the project add the new dependence:</p><pre><code class="language-bash">npm install openai
#or
yarn add openai</code></pre><p>Create a <code>.env</code> file and insert your API key:</p><pre><code class="language-.env">OPENAI_API_KEY=sk-123abc</code></pre><p>In the <code>services</code> folder create the <code>openAIGPT.js</code> file and put this content:</p><pre><code class="language-javascript">import OpenAI from &apos;openai&apos;;

export const searchGPT = async (question) =&gt; {
  const openai = new OpenAI();

  const chatCompletion = await openai.chat.completions.create({
    messages: [{ role: &apos;user&apos;, content: question }],
    model: &apos;gpt-3.5-turbo&apos;,
  });

  return chatCompletion.choices[0].message.content;
}
</code></pre><p>Here we create a <code>searchGPT</code> function and inside we start the <code>OpenAI</code> object, we don&apos;t need to pass any config for this sample cause <a href="https://www.npmjs.com/package/openai?ref=tuliocalil.com#usage" rel="noreferrer">the package automatically search for the env variable</a> with <code>OPENAI_API_KEY</code>.</p><p>In the next line, we use the <code>chat.completions</code> to start new completions, the Chat Completions API is an interface that allows developers to integrate GPT-3&apos;s language generation capabilities into their applications, particularly for generating human-like text in a conversational format. <br>On <code>create</code> we set the message object (you can pass multiple messages) that comes from the user&apos;s request and the role, &quot;rules&quot; refer to specific instructions or guidelines you can provide to the GPT-3 model as part of the API request to control its behavior and output. <br>Finally, we return the response content.</p><p>Go back to the <code>GPTController.js</code> and change to this:</p><pre><code class="language-javascript">import {searchGPT} from &quot;../services/openAIGPT.js&quot;

export const search = async (request) =&gt; {
  const { question } = request.query;

  if(!question){
    return { message: &quot;invalid&quot; }
  }

  const message = await searchGPT(question);
  return { message }
}
</code></pre><p>We get the <code>question</code> from the request query and check if it exists, if not we return an &quot;invalid&quot; message (I strongly recommend using the <a href="https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/?ref=tuliocalil.com#validation" rel="noreferrer">Fastify validators</a>). If exists we call the <code>searchGPT</code> service and create the completion.<br>Running the server and testing again we got: </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/10/image-5.png" class="kg-image" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT" loading="lazy" width="1660" height="1096" srcset="https://tuliocalil.com/content/images/size/w600/2023/10/image-5.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/10/image-5.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/10/image-5.png 1600w, https://tuliocalil.com/content/images/2023/10/image-5.png 1660w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Request to GPT controller in fastify server</span></figcaption></figure><p>I put a query <code>question</code> in the URL and got the GPT response in my body.</p><p>You can mix this knowledge with other things that we already see here in the blog, for example, we can connect your API to an <a href="https://tuliocalil.com/desktop-apps-with-electron-react-and-sqlite/" rel="noreferrer">Electron desktop APP with our SQLite</a> database to generate insights from the data. We can use <a href="https://tuliocalil.com/awesome-electron-uikits-for-2023/" rel="noreferrer">UI Kits</a> to create beautiful interfaces for this integration too.</p><p>In this journey to harness the capabilities of Node.js, Fastify, and OpenAI&apos;s Chat GPT, you&apos;ve unlocked the true potential of modern web development. Your REST API, now infused with the magic of AI-powered conversations, stands as a testament to the endless possibilities at your fingertips. As you venture forward, remember that the collaboration of cutting-edge technologies can redefine user experiences, revolutionize industries, and shape the future. Embrace this fusion of innovation, and continue to create, innovate, and unleash the power of technology to transform the world.</p><p>If you&apos;re interested in exploring the code behind this project, you can find it on GitHub. Feel free to dive in, learn, and continue to build amazing things with these powerful tools.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tuliocll?ref=tuliocalil.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">tuliocll - Overview</div><div class="kg-bookmark-description">Hi &#x1F44B;. tuliocll has 69 repositories available. Follow their code on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT"><span class="kg-bookmark-author">GitHub</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars.githubusercontent.com/u/22357579?v=4?s=400" alt="Unleash the Power of Node.js and Fastify: Build a Rest API and Connect it to Open AI Chat GPT"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Awesome Electron UI Kits for 2023]]></title><description><![CDATA[Are you a developer seeking to elevate your desktop application and web design projects in 2023? Look no further! This post delves into the potential of Electron and UI Kits, offering the perfect blend of efficiency, and consistency.]]></description><link>https://tuliocalil.com/awesome-electron-uikits-for-2023/</link><guid isPermaLink="false">650ca43fb83f2e1c295afbc1</guid><category><![CDATA[Electron]]></category><category><![CDATA[Frontend]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Thu, 21 Sep 2023 21:43:52 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1487338875411-8880f74114a2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHVpJTIwa2l0fGVufDB8fHx8MTY5NTMyNzYyMnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1487338875411-8880f74114a2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHVpJTIwa2l0fGVufDB8fHx8MTY5NTMyNzYyMnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Awesome Electron UI Kits for 2023"><p>In the ever-evolving software development landscape, Electron has emerged as a powerful framework, enabling developers to build cross-platform desktop applications using web technologies. Its flexibility and extensive community support have made it a go-to choice for crafting desktop apps. However, as any developer knows, creating a polished user interface that caters to both aesthetics and accessibility can be a daunting task.</p>
<p>This is where Electron UI Kits will step in as game-changers for developers in 2023. They not only streamline the UI design process but also serve as a crucial tool for ensuring consistency across platforms and improving accessibility for a broader user base.</p>
<p>In this post, we&apos;ll delve into the world of Electron UI Kits and explore how they can supercharge your development workflow. We&apos;ll discuss what Electron is, why UI kits are indispensable, and how they can help accelerate development, standardize the user interface, and make significant strides in enhancing accessibility.</p>
<p>Join us on this journey as we discover the latest and greatest Electron UI Kits for 2023, equipping you with the tools to craft stunning, accessible, and cohesive desktop applications that leave a lasting impression on your users. Whether you&apos;re a seasoned Electron developer or just starting your journey, these UI kits are bound to revolutionize the way you approach desktop application design.</p>
<h2 id="starting-a-demo-electron-project">Starting a demo Electron project</h2>
<p>To test all these UI kits, I will create a simple (with no UI framework) project to test some components, to do that I will use the <a href="https://www.electronforge.io/templates/vite?ref=tuliocalil.com">Electron-forge </a>project, so in a terminal type:</p>
<pre><code class="language-sh">yarn create electron-app my-new-app --template=vite</code></pre>
<p>Open it in your favorite code editor and delete all content in <code>src/index.css</code>.<br>Now we can check the UI Kits!</p>
<h2 id="tailwind"><a href="https://tailwindui.com/?ref=tuliocalil.com">Tailwind</a></h2>
<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/09/image-4.png" class="kg-image" alt="Awesome Electron UI Kits for 2023" loading="lazy" width="2000" height="1358" srcset="https://tuliocalil.com/content/images/size/w600/2023/09/image-4.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/09/image-4.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/09/image-4.png 1600w, https://tuliocalil.com/content/images/2023/09/image-4.png 2188w" sizes="(min-width: 720px) 720px"><figcaption><span>A tailwind UI kit and components in electron project.</span></figcaption></figure>
<p>Tailwind CSS is a utility-first CSS framework that provides a set of pre-designed, reusable CSS classes. Rather than writing custom CSS from scratch, developers can compose web interfaces by applying these classes directly to HTML elements. This approach speeds up development and ensures a consistent design language throughout your application.</p>
<div class="kg-card kg-signup-card kg-width-wide " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span>Sign up for Tulio Calil Dev</span></h2>
                    <h3 class="kg-signup-card-subheading" style="color: #000000;"><span>Building Quality Technological Solutions</span></h3>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span>No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>
<h2 id="langui"><a href="https://www.langui.dev/?ref=tuliocalil.com">Langui</a></h2>
<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/09/image-5.png" class="kg-image" alt="Awesome Electron UI Kits for 2023" loading="lazy" width="2000" height="1358" srcset="https://tuliocalil.com/content/images/size/w600/2023/09/image-5.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/09/image-5.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/09/image-5.png 1600w, https://tuliocalil.com/content/images/2023/09/image-5.png 2186w" sizes="(min-width: 720px) 720px"><figcaption><span>A Langui UI kit and components in electron project.</span></figcaption></figure>
<p>Langui is a Tailwind library with free-to-use components tailored for your AI and GPT projects. Focus on building the next best project and let it handle the UI.<br>With a lot of components based on tailwind, you can create beautiful UIs for your AI products.</p>
<h2 id="pure-css"><a href="https://purecss.io/?ref=tuliocalil.com">Pure CSS</a></h2>
<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/09/image-6.png" class="kg-image" alt="Awesome Electron UI Kits for 2023" loading="lazy" width="2000" height="1356" srcset="https://tuliocalil.com/content/images/size/w600/2023/09/image-6.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/09/image-6.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/09/image-6.png 1600w, https://tuliocalil.com/content/images/2023/09/image-6.png 2186w" sizes="(min-width: 720px) 720px"><figcaption><span>A Purecss in electron project.</span></figcaption></figure>
<p>Pure.css is a minimalistic CSS framework developed by Yahoo. Its primary mission is to provide a set of small, responsive, and unobtrusive CSS modules that you can easily drop into your web projects. Unlike some larger CSS frameworks, Pure.css offers only essential styling, giving you the flexibility to customize your designs according to your unique preferences.</p>
<h2 id="ui-kits-for-react-vue-svelte-or-angular">UI Kits for React, Vue, Svelte, or Angular</h2>
<p>As you can see, we can use almost all UI Kits that we got in the web development in Electron, and this is why Electron is so good.<br>Of course, we can use UI Kits from frameworks, so I link these guys here, you can check:</p>
<blockquote>Some of them can be used in more than one framework</blockquote>
<table>
<thead>
<tr>
<th>Framework</th>
<th>Name/Link</th>
</tr>
</thead>
<tbody>
<tr>
<td>React</td>
<td><a href="https://nextui.org/?ref=tuliocalil.com">NextUI</a></td>
</tr>
<tr>
<td>React</td>
<td><a href="https://chakra-ui.com/?ref=tuliocalil.com">Chakra UI</a></td>
</tr>
<tr>
<td>Vuejs</td>
<td><a href="https://www.radix-vue.com/?ref=tuliocalil.com">Radix</a></td>
</tr>
<tr>
<td>Vuejs</td>
<td><a href="https://buefy.org/?ref=tuliocalil.com">Bluefy</a></td>
</tr>
<tr>
<td>Angular</td>
<td><a href="https://alyle.io/?ref=tuliocalil.com">Alyle</a></td>
</tr>
<tr>
<td>Angular</td>
<td><a href="https://onsen.io/?ref=tuliocalil.com">Onsen</a></td>
</tr>
<tr>
<td>Svelte</td>
<td><a href="https://ikun-ui-docs.vercel.app/?ref=tuliocalil.com">IkunUI</a></td>
</tr>
<tr>
<td>Svelte</td>
<td><a href="https://svelte-headlessui.goss.io/docs/2.0?ref=tuliocalil.com">Headless</a></td>
</tr>
</tbody>
</table>
<p></p>]]></content:encoded></item><item><title><![CDATA[Offline auth with Electron + SQLite + React]]></title><description><![CDATA[<p>In an increasingly digital world, the need for secure and robust authentication systems is paramount. Whether you&apos;re building a desktop application, a mobile app, or a web platform, the ability to authenticate users reliably and securely is a fundamental requirement. While online authentication methods are well-established and widely</p>]]></description><link>https://tuliocalil.com/offline-auth-with-electron-sqlite-react/</link><guid isPermaLink="false">64f3b927b83f2e1c295af9b5</guid><category><![CDATA[Electron]]></category><category><![CDATA[Performance]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sun, 03 Sep 2023 12:42:53 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1489875347897-49f64b51c1f8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHNxbHxlbnwwfHx8fDE2OTM2OTQzNTV8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1489875347897-49f64b51c1f8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHNxbHxlbnwwfHx8fDE2OTM2OTQzNTV8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Offline auth with Electron + SQLite + React"><p>In an increasingly digital world, the need for secure and robust authentication systems is paramount. Whether you&apos;re building a desktop application, a mobile app, or a web platform, the ability to authenticate users reliably and securely is a fundamental requirement. While online authentication methods are well-established and widely adopted, there are situations where offline authentication becomes essential. This is particularly true for desktop applications, where internet connectivity is not always guaranteed.</p>
<p>In this article, we dive into the world of &quot;Offline Authentication&quot; and explore a powerful combination of technologies to achieve this goal: Electron, SQLite, and React. By leveraging the capabilities of Electron, a framework for building cross-platform desktop applications, and harnessing the flexibility of SQLite, a lightweight and embedded database engine, we&apos;ll create a robust offline authentication system. To top it off, we&apos;ll enhance the user experience by incorporating the user-friendly React library for building the application&apos;s front end.</p>
<p>Offline authentication isn&apos;t just about enabling access when the internet is down; it&apos;s about providing users with a seamless and secure experience, even when disconnected. Whether you&apos;re developing a stand-alone desktop application or a part of a broader software ecosystem, the techniques and principles we&apos;ll explore here will empower you to create reliable offline authentication solutions that protect user data and ensure a smooth user experience.</p>
<p>Let&apos;s embark on this journey to learn how to implement offline authentication with Electron, SQLite, and React, and unlock new possibilities for your desktop applications.</p>
<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/09/image.png" class="kg-image" alt="Offline auth with Electron + SQLite + React" loading="lazy" width="2000" height="1366" srcset="https://tuliocalil.com/content/images/size/w600/2023/09/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/09/image.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/09/image.png 1600w, https://tuliocalil.com/content/images/2023/09/image.png 2030w" sizes="(min-width: 720px) 720px"><figcaption><span>Login and Signup pages</span></figcaption></figure>
<p>First, we will use the last <a href="https://tuliocalil.com/desktop-apps-with-electron-react-and-sqlite/">Electron post</a> to start this new one. You can clone the repository on Github:</p>
<figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tuliocll/electron-react-sqlite-todo?ref=tuliocalil.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - tuliocll/electron-react-sqlite-todo: A TODO app with Auth built with Electron, ReactJS and SQLite</div><div class="kg-bookmark-description">A TODO app with Auth built with Electron, ReactJS and SQLite - GitHub - tuliocll/electron-react-sqlite-todo: A TODO app with Auth built with Electron, ReactJS and SQLite</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Offline auth with Electron + SQLite + React"><span class="kg-bookmark-author">tuliocll</span><span class="kg-bookmark-publisher">GitHub</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/f992108d756cbb2be0db06d3f42c5a5157dcd6285a8a952c5cb5035eaeb623e8/tuliocll/electron-react-sqlite-todo" alt="Offline auth with Electron + SQLite + React"></div></a></figure>
<p>Let&apos;s start with our business rules for this new feature. We want to create a register and login pages and check if the user is authenticated before accessing some resources (pages).</p>
<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/09/image-1.png" class="kg-image" alt="Offline auth with Electron + SQLite + React" loading="lazy" width="1054" height="810" srcset="https://tuliocalil.com/content/images/size/w600/2023/09/image-1.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/09/image-1.png 1000w, https://tuliocalil.com/content/images/2023/09/image-1.png 1054w" sizes="(min-width: 720px) 720px"><figcaption><span>Simple diagram</span></figcaption></figure>
<p>We need to create the <code>users</code> table on SQLite database, so open the database on your favorite tool to manage SQLite databases and create the new table:</p>
<pre><code class="language-sql">CREATE TABLE &quot;user&quot; (
	&quot;id&quot;	INTEGER,
	&quot;username&quot;	TEXT,
	&quot;password_hash&quot;	TEXT,
	&quot;status&quot;	INTEGER,
	PRIMARY KEY(&quot;id&quot; AUTOINCREMENT)
);</code></pre>
<p>With the new table created it&apos;s time to create the Typescript types for this entity. Create a new folder called <code>types</code> in the <code>src/</code> folder and create a <code>User.d.ts</code> file with this content:</p>
<pre><code class="language-ts">declare type User = {
  id?: number;
  username: string;
  password_hash: Buffer;
  status: number;
};

declare type Auth = {
  username: string;
  password: string;
};
</code></pre>
<p>This declaration permits us to use it on both processes, main and render.</p>
<p>To help us with the security, let&apos;s use the <code>safeStorage</code> module from <a href="https://www.electronjs.org/pt/docs/latest/api/safe-storage?ref=tuliocalil.com">electron core</a>. This package lets us encrypt and decrypt data and protects it from being accessed by other applications using the native OS resources (Keychain and password manager for example).</p>
<p>Create a new <code>util</code> inside <code>utils</code> folder on the <code>main</code> process side called <code>encrypt.ts</code> and put this code:</p>
<pre><code class="language-ts">import { safeStorage } from &apos;electron&apos;;

export function hashText(text: string) {
  try {
    if (!safeStorage.isEncryptionAvailable()) {
      return false;
    }

    return safeStorage.encryptString(text);
  } catch (err) {
    return false;
  }
}

export function decriptText(text: Buffer) {
  try {
    if (!safeStorage.isEncryptionAvailable()) {
      return false;
    }

    return safeStorage.decryptString(text);
  } catch (err) {
    return false;
  }
}
</code></pre>
<p>This code verifies if the encryption is available and uses it.</p>
]]></content:encoded></item><item><title><![CDATA[Desktop Apps with Electron, React and SQLite]]></title><description><![CDATA[Learn how to create efficient and powerful desktop applications using Electron, React, and SQLite. Discover the benefits of this winning combination and enhance your productivity today.]]></description><link>https://tuliocalil.com/desktop-apps-with-electron-react-and-sqlite/</link><guid isPermaLink="false">64b423284144d6b225455df2</guid><category><![CDATA[React]]></category><category><![CDATA[Nodejs]]></category><category><![CDATA[Electron]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sun, 09 Jul 2023 19:01:40 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1619410283995-43d9134e7656?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHJlYWN0anN8ZW58MHx8fHwxNjkyMTg3MTkzfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1619410283995-43d9134e7656?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHJlYWN0anN8ZW58MHx8fHwxNjkyMTg3MTkzfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Desktop Apps with Electron, React and SQLite"><p>This post is an improvement of <a href="https://tuliocalil.com/app-desktop-com-react-electron-sqlite-ms-to-do-clone/" rel="noopener">this post (in Portuguese)</a> and the last part, covers the SQLite integration.<br>First, let&apos;s start from the beginning (again) without wasting any more time.</p><blockquote>I made a small update on this post at out 2023 to fixes the build with electron and SQLite, <a href="https://github.com/tuliocll/electron-react-sqlite-todo/issues/3?ref=tuliocalil.com" rel="noreferrer">check the discus here</a>.</blockquote><h2 id="project-overview">Project Overview</h2><p>A TO-DO is an awesome idea for this project, so let&apos;s create something like the &quot;Microsoft To-do&quot;.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/07/krv8zxfof74mhiutzjkc.png" class="kg-image" alt="Desktop Apps with Electron, React and SQLite" loading="lazy" width="1920" height="1080" srcset="https://tuliocalil.com/content/images/size/w600/2023/07/krv8zxfof74mhiutzjkc.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/07/krv8zxfof74mhiutzjkc.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/07/krv8zxfof74mhiutzjkc.png 1600w, https://tuliocalil.com/content/images/2023/07/krv8zxfof74mhiutzjkc.png 1920w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">No, this is not the final project.</span></figcaption></figure><p>We will use:</p><ul><li>Electron is an open-source project that allows developers to create Desktop apps with Web technologies.</li><li>React, your UI/Components library.</li><li>SQLite, a portable SQL database.</li></ul><h2 id="starting-the-project">Starting the project</h2><p>To accelerate your development, let&apos;s use a <a href="https://electron-react-boilerplate.js.org/?ref=tuliocalil.com" rel="noopener">React + Electron boilerplate</a>, this will save a lot of time of setup and organize your base project.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># Clone the boilerplate:
git clone --depth=1 \
  https://github.com/electron-react-boilerplate/electron-react-boilerplate \
  todo-clone

cd todo-clone

# Install dependencies:
npm install</code></pre><figcaption><p><span style="white-space: pre-wrap;">Cloning your boilerplate and install de inicial dependencies.</span></p></figcaption></figure><p>After installation was complete, you can run <code>npm start</code> and see the default boilerplate page:</p><figure class="kg-card kg-image-card"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rjc6vgeytb9ybqr314i.png" class="kg-image" alt="Desktop Apps with Electron, React and SQLite" loading="lazy"></figure><p>If we open the project in a code editor, we will see this folder structure: </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xzzza84wr1741fr9iti.png" class="kg-image" alt="Desktop Apps with Electron, React and SQLite" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Yeah, a lot of files</span></figcaption></figure><p>By default, the boilerplate comes with <a href="https://github.com/css-modules/css-modules?ref=tuliocalil.com">CSS Modules,</a> <a href="https://reactrouter.com/en/main?ref=tuliocalil.com">React Router Dom</a>, and the <a href="https://testing-library.com/?ref=tuliocalil.com">testing-library</a>.</p><div class="kg-card kg-product-card">
            <div class="kg-product-card-container">
                <img src="https://tuliocalil.com/content/images/2023/10/619ht2WrGTL._SL1045_.jpg" width="800" height="1045" class="kg-product-card-image" loading="lazy" alt="Desktop Apps with Electron, React and SQLite">
                <div class="kg-product-card-title-container">
                    <h4 class="kg-product-card-title"><span style="white-space: pre-wrap;">Clean Architecture</span></h4>
                </div>
                
                    <div class="kg-product-card-rating">
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class="kg-product-card-rating-active kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                        <span class=" kg-product-card-rating-star"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M12.729,1.2l3.346,6.629,6.44.638a.805.805,0,0,1,.5,1.374l-5.3,5.253,1.965,7.138a.813.813,0,0,1-1.151.935L12,19.934,5.48,23.163a.813.813,0,0,1-1.151-.935L6.294,15.09.99,9.837a.805.805,0,0,1,.5-1.374l6.44-.638L11.271,1.2A.819.819,0,0,1,12.729,1.2Z"/></svg></span>
                    </div>
                

                <div class="kg-product-card-description"><p><b><strong style="white-space: pre-wrap;">$17,00</strong></b></p></div>
                
                    <a href="https://amzn.to/46SZwSe?ref=tuliocalil.com" class="kg-product-card-button kg-product-card-btn-accent" target="_blank" rel="noopener noreferrer"><span>GET OFFER</span></a>
                
            </div>
        </div><h2 id="starting-the-ui">Starting the UI</h2><p>Let&apos;s start organizing the project to place our files in the right place, first, inside of <code>src/renderer</code> create three folders: <code>components</code>, <code>styles</code>, and <code>views</code>.<br>Now delete the <code>App.tsx</code> and create the <code>routes.tsx</code>, rename the <code>App.css</code> to <code>App.global.css</code>, and move to the <code>styles</code> folder.<br>Inside the <code>views</code> folder, create the <code>Home</code> folder and inside it creates <code>index.tsx</code>, and <code>Home.module.css</code>.<br>Create <code>default.css</code> inside <code>styles/themes</code> folder (create the <code>themes</code> folder too).<br>Now, let&apos;s see the code for each file:</p><p><code>src/renderer/styles/App.global.css</code>:</p><pre><code class="language-css">@import &apos;./themes/default.css&apos;;

* {
  padding: 0;
  margin: 0;
  font-family: var(--font);
}

.button {
  background-color: var(--primary-color);
  border: none;
  margin: 5px;
  font-size: 0.7rem;
  padding: 5px 10px;
  border-radius: 4px;
  color: var(--text-color);
}

.button:hover {
  opacity: 0.8;
  cursor: pointer;
}

.button + .secondary {
  background-color: var(--text-cancel-color);
}
</code></pre><p><code>src/renderer/styles/themes/default.css</code>:</p><pre><code class="language-css">@import url(&apos;https://fonts.googleapis.com/css2?family=Roboto:wght@100;300&amp;display=swap&apos;);

:root {
  --primary-color: #788cde;
  --secondary-color: #323232;
  --background-color: #282828;
  --alternate-background-color: #1e1e1e;

  --text-color: #e1e1e1;
  --text-color-light: #777676bb;
  --font: Roboto;

  --text-cancel-color: #dd2a2c;

  --link-color: #e1e1e1;
  --link-color--hover: #543fd7;
}
</code></pre><p><code>src/renderer/index.tsx</code>:</p><pre><code class="language-jsx">import &apos;./styles/App.global.css&apos;;
import { createRoot } from &apos;react-dom/client&apos;;
import Routes from &apos;./routes&apos;;

const container = document.getElementById(&apos;root&apos;) as HTMLElement;
const root = createRoot(container);
root.render(&lt;Routes /&gt;);
</code></pre><p><code>src/renderer/routes.tsx</code>:</p><pre><code class="language-jsx">import { MemoryRouter, Route, Routes } from &apos;react-router-dom&apos;;

import Home from &apos;./views/Home&apos;;

export default function appRoutes() {
  return (
    &lt;MemoryRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; Component={Home} /&gt;
      &lt;/Routes&gt;
    &lt;/MemoryRouter&gt;
  );
}
</code></pre><p>Now, let&apos;s see the components&apos; part, inside <code>components</code> create these folders; <code>Sidebar</code>, <code>Logo</code>, <code>TaskArea</code> and <code>TaskItem</code>. For each one, create the <code>index.tsx</code> and <code>{ComponenteName}.module.tsx</code>.</p><p><code>src/renderer/components/Sidebar/index.tsx</code>:</p><pre><code class="language-jsx">import Logo from &apos;../Logo&apos;;

import styles from &apos;./Sidebar.module.css&apos;;

export default function Sidebar() {
  return (
    &lt;div className={styles.sidenav}&gt;
      &lt;Logo /&gt;
      &lt;a href=&quot;#&quot;&gt;Meu dia&lt;/a&gt;
      &lt;a href=&quot;#&quot;&gt;Importante&lt;/a&gt;
      &lt;a href=&quot;#&quot;&gt;Planejado&lt;/a&gt;
      &lt;a href=&quot;#&quot;&gt;Trabalho&lt;/a&gt;
    &lt;/div&gt;
  );
}
</code></pre><p><code>src/renderer/components/Sidebar/Sidebar.module.css</code>:</p><pre><code class="language-css">@import &apos;../../styles/themes/default.css&apos;;

.sidenav {
  width: 240px;
  height: 100vh;
  background: var(--background-color);
  overflow-x: hidden;
  padding-left: 10px;
}

.sidenav a {
  padding: 10px;
  text-decoration: none;
  font-family: var(--font);
  font-size: 1.1rem;
  color: var(--link-color);
  display: block;
}

.sidenav a:hover {
  background-color: var(--alternate-background-color);
}</code></pre><p>Now is the time to create our Task components, create <code>components/TaskArea</code> and <code>components/TaskItem</code>. Rember to create our default files (index and CSS).</p><p><code>TaskArea/index.tsx</code>:</p><pre><code class="language-jsx">import TaskItem from &apos;../TaskItem&apos;;

import styles from &apos;./TaskArea.module.css&apos;;

export type TODO = {
  id?: number;
  title: string;
  date: string;
  status: number;
};

export default function TaskArea({
  todos,
  onCheck,
  onDelete,
  onEdit,
}: {
  todos: TODO[];
  onCheck: (id: number) =&gt; void;
  onDelete: (id: number) =&gt; void;
  onEdit: (id: number) =&gt; void;
}) {
  return (
    &lt;div className={styles.container}&gt;
      {todos.map((todo) =&gt; (
        &lt;TaskItem
          checked={todo.status === 1 ? true : false}
          date={todo.date}
          label={todo.title}
          key={todo.id}
          id={todo.id}
          onChange={onCheck}
          onDelete={onDelete}
          onEdit={onEdit}
        /&gt;
      ))}
    &lt;/div&gt;
  );
}</code></pre><p><code>TaskArea/TaskArea.module.css</code>:</p><pre><code class="language-css">@import &apos;../../styles/themes/default.css&apos;;

.container {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 10px;
  background-color: var(--alternate-background-color);
}
</code></pre><p><code>TaskItem/index.tsx</code>:</p><pre><code class="language-jsx">import { format } from &apos;date-fns&apos;;
import styles from &apos;./TaskItem.module.css&apos;;

export type TaskItem = {
  label: string;
  date: string;
  id: number;
  checked: boolean;
  onChange: (id: number) =&gt; void;
  onEdit: (id: number) =&gt; void;
  onDelete: (id: number) =&gt; void;
};

export default function TaskItem({
  date,
  label,
  id,
  checked,
  onChange,
  onDelete,
  onEdit,
}: TaskItem) {
  function handleCheck() {
    onChange(id);
  }

  function handleEdit() {
    onEdit(id);
  }

  function handleDelete() {
    onDelete(id);
  }

  return (
    &lt;div
      className={`${styles.container} ${checked ? styles[&apos;task-finish&apos;] : &apos;&apos;}`}
      id={`${id}`}
    &gt;
      &lt;input
        className={styles.checkbox}
        type=&quot;checkbox&quot;
        checked={checked}
        onChange={handleCheck}
      /&gt;
      &lt;div className=&quot;col&quot;&gt;
        &lt;p className={styles[&apos;task-label&apos;]}&gt;{label}&lt;/p&gt;
        &lt;p className={styles[&apos;task-date&apos;]}&gt;
          {format(new Date(date), &quot;E., dd &apos;de&apos; MMM&quot;)}
        &lt;/p&gt;
        &lt;div&gt;
          &lt;button className=&quot;button&quot; onClick={handleEdit}&gt;
            Editar
          &lt;/button&gt;
          &lt;button className=&quot;button secondary&quot; onClick={handleDelete}&gt;
            Deletar
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre><p><code>TaskItem/TaskItem.module.css</code> </p><pre><code class="language-css">@import &apos;../../styles/themes/default.css&apos;;

.container {
  display: flex;
  align-items: center;
  background-color: var(--secondary-color);
  padding: 10px 20px;
  margin: 1px 0px;
  color: var(--text-color);
  font-family: var(--font);
  border-radius: 6px;
}

.container &gt; :nth-child(1) {
  margin-right: 15px;
}

.task-label {
  font-size: 0.85rem;
  color: var(--text-color);
}

.task-date {
  font-size: 0.85rem;
  color: var(--text-cancel-color);
  font-weight: bold;
}

.task-finish .task-label {
  text-decoration: line-through;
}

input[type=&apos;checkbox&apos;] {
  -webkit-appearance: none;
  appearance: none;
  background-color: var(--alternate-background-color);
  margin: 0;
  font: inherit;
  color: currentColor;
  width: 1.35em;
  height: 1.35em;
  border: 0.15em solid var(--background-color);
  border-radius: 50px;
  transform: translateY(-0.075em);
  display: grid;
  place-content: center;
}

input[type=&apos;checkbox&apos;]::before {
  content: &apos;&apos;;
  width: 0.55em;
  height: 0.55em;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  border-radius: 50px;
  transform: scale(0);
  transform-origin: bottom left;
  transition: 120ms transform ease-in-out;
  box-shadow: inset 1em 1em var(--background-color);
  background-color: var(--background-color);
}

input[type=&apos;checkbox&apos;]:checked::before {
  transform: scale(1);
}

input[type=&apos;checkbox&apos;]:checked {
  background-color: var(--primary-color);
}

input[type=&apos;checkbox&apos;]:focus {
  outline: max(2px, 0.15em) solid currentColor;
  outline-offset: max(2px, 0.15em);
}

input[type=&apos;checkbox&apos;]:disabled {
  color: var(--primary-color);
  cursor: not-allowed;
}
</code></pre><p>Create the <code>Logo</code> componente (folder, files etc):</p><pre><code class="language-jsx">import styles from &apos;./Logo.module.css&apos;;

export default function Logo() {
  return &lt;h1 className={styles.logo}&gt;TODO Clone&lt;/h1&gt;;
}
</code></pre><pre><code class="language-css">@import &apos;../../styles/themes/default.css&apos;;

.logo {
  color: var(--primary-color);
  margin: 20px 0px;
  font-family: var(--font);
  font-weight: 800;
}
</code></pre><p>The <code>Modal</code> component:</p><pre><code class="language-tsx">import { useState } from &apos;react&apos;;
import styles from &apos;./Modal.module.css&apos;;
import { TODO } from &apos;../TaskArea&apos;;
export type Modal = {
  initialData: TODO | undefined;
  onClose: () =&gt; void;
  onSave: (item: TODO) =&gt; void;
};

export default function Modal({ onClose, initialData, onSave }: Modal) {
  const [title, setTitle] = useState(initialData?.title || &apos;&apos;);

  function handleOnSave() {
    if (title === &apos;&apos;) {
      alert(&apos;Invalid title&apos;);
      return;
    }

    onSave({
      title,
      date: initialData?.date || new Date().toLocaleString(),
      status: initialData?.status || 0,
      id: initialData?.id,
    });
  }

  return (
    &lt;div className={styles.modal}&gt;
      &lt;div className={styles.modal_content}&gt;
        &lt;span className={styles.close} onClick={onClose}&gt;
          &amp;times;
        &lt;/span&gt;
        &lt;h2&gt;New task&lt;/h2&gt;
        &lt;div className={styles.formGroup}&gt;
          &lt;label&gt;Title&lt;/label&gt;
          &lt;input value={title} onChange={(el) =&gt; setTitle(el.target.value)} /&gt;
        &lt;/div&gt;
        &lt;button className=&quot;button&quot; onClick={handleOnSave}&gt;
          Save
        &lt;/button&gt;
        &lt;button className=&quot;button secondary&quot; onClick={onClose}&gt;
          Cancel
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre><pre><code class="language-css">@import &apos;../../styles/themes/default.css&apos;;


.modal {
  display: block; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 100px; /* Location of the box */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
  color: var(--text-color);
}

.modal_content {
  background-color: var(--background-color);
  margin: auto;
  padding: 20px;
  border: 1px solid var(--primary-color);
  width: 50%;
}

/* The Close Button */
.close {
  color: var(--text-cancel-color);
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

.formGroup {
display:flex;
flex-direction: column;
margin: 10px 0px
}

.formGroup &gt; input {
    font-size: 1rem;
    padding: 5px;
    margin: 5px 0px;
    border: 1px solid;
    border-radius: 4px;
    border-color: var(--primary-color);
    background-color: var(--secondary-color);
    color: var(--text-color);
}
</code></pre><p>Back to the <code>views</code> folder, let&apos;s put the Home code:</p><pre><code class="language-jsx">import TaskArea, { TODO } from &apos;../../components/TaskArea&apos;;
import Sidebar from &apos;../../components/Sidebar&apos;;
import FAB from &apos;../../components/FAB&apos;;
import Modal from &apos;../../components/Modal&apos;;

import styles from &apos;./Home.module.css&apos;;
import { useState } from &apos;react&apos;;

export default function Home() {
  const [modal, setModal] = useState(false);
  const [todos, setTodos] = useState&lt;TODO[]&gt;([
    {
      id: 1,
      date: new Date().toLocaleString(),
      status: 1,
      title: &apos;Test&apos;,
    },
    {
      id: 2,
      date: new Date().toLocaleString(),
      status: 0,
      title: &apos;Test 2&apos;,
    },
  ]);
  const [edit, setEdit] = useState&lt;TODO&gt;();

  function onSave(todo: TODO) {
    const update = todos.find((el) =&gt; el.id === todo.id);
    if (update) {
      const updatedTodos = todos.map((el) =&gt; {
        if (el.id === todo.id) {
          return todo;
        }

        return el;
      });
      setTodos(updatedTodos);
    }

    setTodos([...todos, todo]);
    toggleModal();
  }

  const onCheck = (id: number) =&gt; {
    const newState = todos.map((todo) =&gt; {
      if (todo.id === id) {
        return {
          ...todo,
          status: todo.status === 1 ? 0 : 1,
        };
      }

      return todo;
    });

    setTodos(newState);
  };

  function onDelete(id: number) {
    setTodos(todos.filter((todo) =&gt; todo.id !== id));
  }

  function onEdit(id: number) {
    const editTodo = todos.find((todo) =&gt; todo.id === id);
    if (editTodo) {
      setEdit(editTodo);
    }

    toggleModal();
  }

  function toggleModal() {
    if (modal) {
      setEdit(undefined);
    }

    setModal(!modal);
  }

  return (
    &lt;div className={styles.container}&gt;
      &lt;Sidebar /&gt;
      &lt;FAB onClick={toggleModal} /&gt;
      {modal &amp;&amp; (
        &lt;Modal onClose={toggleModal} onSave={onSave} initialData={edit} /&gt;
      )}
      &lt;TaskArea
        todos={todos}
        onCheck={onCheck}
        onDelete={onDelete}
        onEdit={onEdit}
      /&gt;
    &lt;/div&gt;
  );
}
</code></pre><pre><code class="language-css">.container {
  display: flex;
  flex-direction: row;
}
</code></pre><p>Go to your terminal and run the <code>start</code> script:</p><pre><code class="language-bash">yarn start
#or
npm script</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/07/image-2.png" class="kg-image" alt="Desktop Apps with Electron, React and SQLite" loading="lazy" width="1458" height="1452" srcset="https://tuliocalil.com/content/images/size/w600/2023/07/image-2.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/07/image-2.png 1000w, https://tuliocalil.com/content/images/2023/07/image-2.png 1458w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">WOW, NICE GRAPHICS</span></figcaption></figure><blockquote>This is what we have from the previous post (with many improvements).</blockquote><h2 id="sqlite">SQLite</h2><p>It&apos;s time to create and connect to an SQLite database and create the CRUD for the TODOs. At this point I need to explain a little bit about <a href="https://www.electronjs.org/docs/latest/glossary?ref=tuliocalil.com#process" rel="noopener">Electron process</a>, in Electron we have two kinds of process, the <a href="https://www.electronjs.org/docs/latest/glossary?ref=tuliocalil.com#main-process" rel="noopener">main process</a> and the <a href="https://www.electronjs.org/docs/latest/glossary?ref=tuliocalil.com#renderer-process" rel="noopener">renderer process</a>.</p><ul><li>Main process: It&apos;s the entry point of every Electron application, controlling the life cycle, native elements, spawn new renderer process, and more. We have full Nodejs API. Is like the &quot;backend&quot; part of our project. </li><li>Renderer process: This is the UI part; we can think about it like this is a &quot;frontend&quot; part of your project. We don&apos;t have access to full Nodejs APIs, like file systems, DB connections, or something like that.</li></ul><p>So, who we can communicate between this process? With <a href="https://www.electronjs.org/docs/latest/glossary?ref=tuliocalil.com#ipc" rel="noopener">IPC</a>! Electron use inter-process communication to send and receive serialized JSON between the main and renderer processes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tuliocalil.com/content/images/2023/07/data-src-image-51a21f47-2786-4214-bcd3-b47ce05f39e5.png" class="kg-image" alt="Desktop Apps with Electron, React and SQLite" loading="lazy" width="440" height="114"><figcaption><span style="white-space: pre-wrap;">Image from: </span><a href="https://blog.logrocket.com/electron-ipc-response-request-architecture-with-typescript/?ref=tuliocalil.com"><span style="white-space: pre-wrap;">https://blog.logrocket.com/electron-ipc-response-request-architecture-with-typescript/</span></a></figcaption></figure>]]></content:encoded></item><item><title><![CDATA[React Native UI Kit for 2023]]></title><description><![CDATA[A list of react native ui kit and components libraries to boost your mobile development with beautiful components!]]></description><link>https://tuliocalil.com/react-native-ui-kit-for-2023/</link><guid isPermaLink="false">64b423284144d6b225455df1</guid><category><![CDATA[React Native]]></category><category><![CDATA[Frontend]]></category><dc:creator><![CDATA[Tulio Calil]]></dc:creator><pubDate>Sat, 01 Jul 2023 17:55:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1581287053822-fd7bf4f4bfec?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHVpfGVufDB8fHx8MTY4OTM1ODUzOHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1581287053822-fd7bf4f4bfec?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHVpfGVufDB8fHx8MTY4OTM1ODUzOHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="React Native UI Kit for 2023"><p>In 2021 I posted the &quot;<a href="https://tuliocalil.com/awesome-react-native-ui-kits-for-2021/" rel="noopener">Awesome React Native UI Kits for 2021</a>&quot; and this is one of the most popular posts in this blog until today, and it&apos;s time to see what news we have in 2023 for UI in React Native!</p><h2 id="rnui">RNUI</h2><figure class="kg-card kg-image-card"><img src="https://wix.github.io/react-native-ui-lib/assets/images/showcase-7560b837a47a1abd0b6318eee74e972e.jpg" class="kg-image" alt="React Native UI Kit for 2023" loading="lazy"></figure><p><a href="https://wix.github.io/react-native-ui-lib/?ref=tuliocalil.com" rel="noopener">RNUI</a> is a component library and toolset for React Native, with beautiful components out-of-the-box, accessibility support and much more!</p><h2 id="shoutem-ui">Shoutem UI</h2><figure class="kg-card kg-image-card"><img src="https://shoutem.github.io/img/ui-toolkit/introduction@2x.jpg" class="kg-image" alt="React Native UI Kit for 2023" loading="lazy"></figure><p><a href="https://shoutem.github.io/docs/ui-toolkit/introduction?ref=tuliocalil.com" rel="noopener">Shoutem UI</a> is part of Shoutem, a platform to create, publish and manage React Native apps.<br>We have a lot of components, theme support, and ready-to-use animations!</p><h2 id="magnus-ui">Magnus UI</h2><figure class="kg-card kg-image-card"><img src="https://tuliocalil.com/content/images/2023/07/image.png" class="kg-image" alt="React Native UI Kit for 2023" loading="lazy" width="1884" height="1202" srcset="https://tuliocalil.com/content/images/size/w600/2023/07/image.png 600w, https://tuliocalil.com/content/images/size/w1000/2023/07/image.png 1000w, https://tuliocalil.com/content/images/size/w1600/2023/07/image.png 1600w, https://tuliocalil.com/content/images/2023/07/image.png 1884w" sizes="(min-width: 720px) 720px"></figure><p>This one is not exactly a UI Kit, but a UI Framework. If you are familiar with frameworks like Tailwind CSS or Bootstrap you will love it! <br>With atomic design methodology, won icon system, theme support and a lot of components, we can create yourself design system using the <a href="https://magnus-ui.com/?ref=tuliocalil.com" rel="noopener">Magnus UI</a>!</p><!--members-only-->]]></content:encoded></item></channel></rss>