<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bruno Mestres</title>
    <description>The latest articles on DEV Community by Bruno Mestres (@brunomestres).</description>
    <link>https://dev.to/brunomestres</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1201765%2Fc32ea846-ebd6-45b2-bb73-f191e51a7ea4.jpeg</url>
      <title>DEV Community: Bruno Mestres</title>
      <link>https://dev.to/brunomestres</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brunomestres"/>
    <language>en</language>
    <item>
      <title>GraphQL: o que é e por que ele importa nas APIs?</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Mon, 26 Oct 2026 17:33:48 +0000</pubDate>
      <link>https://dev.to/brunomestres/graphql-o-que-e-e-por-que-ele-importa-nas-apis-4j9h</link>
      <guid>https://dev.to/brunomestres/graphql-o-que-e-e-por-que-ele-importa-nas-apis-4j9h</guid>
      <description>&lt;p&gt;Imagine uma tela que precisa exibir apenas o nome e a foto de um usuário.&lt;/p&gt;

&lt;p&gt;Ao consultar a API, ela recebe também endereço, telefone, data de nascimento e vários outros campos que não serão utilizados.&lt;/p&gt;

&lt;p&gt;Agora pense em outra tela que precisa dos dados desse usuário e dos títulos dos seus últimos artigos. Dependendo de como a API foi construída, isso pode exigir várias requisições.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GraphQL permite que o cliente descreva os dados de que precisa.&lt;/strong&gt; Essa é uma das suas principais características.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é GraphQL?
&lt;/h2&gt;

&lt;p&gt;GraphQL é uma linguagem de consulta para APIs, acompanhada de um mecanismo para executar essas consultas.&lt;/p&gt;

&lt;p&gt;O servidor define os tipos, campos e operações disponíveis em um contrato chamado &lt;strong&gt;schema&lt;/strong&gt;. Dentro desse contrato, o cliente escolhe quais campos deseja receber.&lt;/p&gt;

&lt;p&gt;Por exemplo, uma consulta poderia ser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;foto&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E a resposta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"usuario"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"nome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bruno"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"foto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://exemplo.com/foto.png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A estrutura da resposta acompanha os campos solicitados. Se outra tela precisar de mais informações, poderá pedi-las, desde que estejam disponíveis no schema e seu acesso seja permitido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que isso é importante?
&lt;/h2&gt;

&lt;p&gt;Uma mesma API pode atender um aplicativo, um site e um painel administrativo. Cada um pode precisar de uma combinação diferente de dados.&lt;/p&gt;

&lt;p&gt;Com GraphQL, esses clientes conseguem adaptar suas consultas às necessidades de cada tela. Também é possível consultar dados relacionados em uma única requisição, como um usuário e seus artigos. Isso pode reduzir tanto o excesso de dados na resposta quanto a quantidade de chamadas necessárias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O ganho está na flexibilidade de consumir a API.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  E os cuidados?
&lt;/h2&gt;

&lt;p&gt;Essa flexibilidade também exige controle. Uma consulta pode pedir muitos dados e percorrer várias relações, gerando um custo alto para o servidor.&lt;/p&gt;

&lt;p&gt;Por isso, uma implementação precisa cuidar de autorização, paginação e limites para as consultas. Receber apenas os campos solicitados não garante, por si só, uma execução barata no backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por onde começar?
&lt;/h2&gt;

&lt;p&gt;Para um primeiro contato, vale guardar esta ideia:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O servidor define o que pode ser consultado, e o cliente descreve o que precisa receber.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A partir daí, o próximo passo é entender como o schema organiza esses dados e como as funções chamadas &lt;strong&gt;resolvers&lt;/strong&gt; buscam as informações para montar a resposta.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>node</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Circuit Breaker no NestJS com @nestjs/resilience</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Fri, 23 Oct 2026 13:07:43 +0000</pubDate>
      <link>https://dev.to/brunomestres/circuit-breaker-no-nestjs-com-nestjsresilience-4460</link>
      <guid>https://dev.to/brunomestres/circuit-breaker-no-nestjs-com-nestjsresilience-4460</guid>
      <description>&lt;p&gt;Quando trabalhamos com APIs, é comum depender de serviços externos para executar determinadas operações.&lt;/p&gt;

&lt;p&gt;Imagine uma aplicação de e-commerce que precisa se comunicar com uma transportadora para solicitar o envio de um pedido.&lt;/p&gt;

&lt;p&gt;Mas o que acontece se a API da transportadora começar a apresentar falhas?&lt;/p&gt;

&lt;p&gt;Uma alternativa para lidar com esse cenário é utilizar o &lt;strong&gt;Circuit Breaker&lt;/strong&gt;, um padrão de resiliência que permite interromper temporariamente chamadas a um serviço quando ele apresenta falhas recorrentes.&lt;/p&gt;

&lt;p&gt;No NestJS, podemos implementar esse comportamento de maneira declarativa utilizando o decorator &lt;code&gt;@CircuitBreaker&lt;/code&gt; do módulo &lt;code&gt;@nestjs/resilience&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementando o Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;Vamos considerar um endpoint responsável por solicitar o despacho de uma entrega:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id/dispatch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CircuitBreaker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;carrier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minimumCalls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;failureRateThreshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;slidingWindow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;openDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shippingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, aplicamos o Circuit Breaker diretamente ao método responsável por iniciar o despacho.&lt;/p&gt;

&lt;p&gt;O decorator recebe algumas configurações que determinam quando o circuito deve ser aberto e por quanto tempo as chamadas serão interrompidas.&lt;/p&gt;

&lt;p&gt;Vamos entender cada uma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name:&lt;/strong&gt; identifica o circuito como &lt;code&gt;carrier&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;minimumCalls:&lt;/strong&gt; define o mínimo de chamadas necessárias para avaliar a taxa de falhas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;failureRateThreshold:&lt;/strong&gt; estabelece a porcentagem de falhas necessária para abrir o circuito. Nesse caso, 50%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;slidingWindow:&lt;/strong&gt; define uma janela de avaliação baseada nas últimas 2 chamadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;openDuration:&lt;/strong&gt; determina que o circuito permaneça aberto por 5 segundos antes de permitir uma tentativa de recuperação.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com essa configuração, se uma das duas chamadas falhar, o circuito poderá ser aberto, pois a taxa de falhas atingiu 50%.&lt;/p&gt;

&lt;p&gt;Enquanto estiver aberto, novas chamadas protegidas pelo circuito serão rejeitadas sem executar o método.&lt;/p&gt;

&lt;p&gt;Após os 5 segundos, o circuito poderá entrar no estado &lt;em&gt;Half-Open&lt;/em&gt;, permitindo verificar se o serviço voltou a funcionar normalmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que ganhamos com essa abordagem?
&lt;/h2&gt;

&lt;p&gt;Uma das principais vantagens é conseguir aplicar uma estratégia de resiliência sem precisar implementar manualmente toda a lógica de controle de falhas.&lt;/p&gt;

&lt;p&gt;Também conseguimos manter o código mais organizado, separando a regra de negócio da configuração do Circuit Breaker.&lt;/p&gt;

&lt;p&gt;Entretanto, vale lembrar que &lt;strong&gt;o Circuit Breaker não resolve a indisponibilidade do serviço externo&lt;/strong&gt;. Ele apenas evita que continuemos realizando chamadas para uma dependência que apresenta problemas.&lt;/p&gt;

&lt;p&gt;Além disso, a configuração utilizada no exemplo é bastante sensível, já que considera apenas duas chamadas. Em produção, os valores devem ser ajustados de acordo com o volume de requisições e o comportamento esperado da aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;@nestjs/resilience&lt;/code&gt; oferece uma maneira prática de adicionar mecanismos de resiliência às aplicações NestJS.&lt;/p&gt;

&lt;p&gt;Com poucas configurações, conseguimos proteger operações que dependem de serviços externos e reduzir o impacto de falhas recorrentes.&lt;/p&gt;

&lt;p&gt;O mais importante não é simplesmente adicionar um Circuit Breaker, mas entender &lt;strong&gt;quando utilizá-lo e como configurar seus limites&lt;/strong&gt; para evitar que uma proteção acabe prejudicando a disponibilidade da própria aplicação.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>architecture</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fingerprinting: o que seu navegador revela sobre você?</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Thu, 22 Oct 2026 00:24:52 +0000</pubDate>
      <link>https://dev.to/brunomestres/fingerprinting-o-que-seu-navegador-revela-sobre-voce-40ia</link>
      <guid>https://dev.to/brunomestres/fingerprinting-o-que-seu-navegador-revela-sobre-voce-40ia</guid>
      <description>&lt;p&gt;Quando acessamos um site, nosso navegador pode compartilhar mais informações do que imaginamos.&lt;/p&gt;

&lt;p&gt;Sistema operacional, resolução da tela, idioma, fuso horário e até características da renderização gráfica são alguns exemplos.&lt;/p&gt;

&lt;p&gt;Individualmente, essas informações podem parecer pouco relevantes. Mas, quando combinadas, podem formar uma espécie de impressão digital do navegador, conhecida como &lt;strong&gt;Browser Fingerprinting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Essa técnica pode ser utilizada tanto para melhorar a segurança de aplicações quanto para rastrear usuários, mesmo sem depender de cookies ou autenticação.&lt;/p&gt;

&lt;p&gt;Mas como isso funciona na prática?&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona o Browser Fingerprinting?
&lt;/h2&gt;

&lt;p&gt;O Browser Fingerprinting consiste em coletar características do navegador e do ambiente em que ele está sendo executado.&lt;/p&gt;

&lt;p&gt;Essas informações são combinadas para criar uma identificação que pode ajudar a reconhecer o mesmo ambiente em diferentes acessos.&lt;/p&gt;

&lt;p&gt;Existem diversas técnicas para isso, mas vamos conhecer duas abordagens: a coleta de informações via JavaScript e o Canvas Fingerprinting.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Coletando informações com JavaScript
&lt;/h3&gt;

&lt;p&gt;Uma das formas mais simples de construir um fingerprint é utilizar APIs disponibilizadas pelo próprio navegador.&lt;/p&gt;

&lt;p&gt;Com poucas linhas de JavaScript, conseguimos coletar algumas características do ambiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDeviceInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolvedOptions&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;screenWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;screenHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hardwareConcurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getDeviceInfo&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, estamos coletando informações como navegador, plataforma, idioma, fuso horário, resolução da tela e quantidade de processadores lógicos disponibilizada pelo navegador.&lt;/p&gt;

&lt;p&gt;Esses dados podem ser combinados e processados por uma função de hash, como SHA-256, para gerar uma identificação.&lt;/p&gt;

&lt;p&gt;Entretanto, &lt;strong&gt;o hash não garante que um dispositivo seja identificado de forma única&lt;/strong&gt;. Dois ambientes podem apresentar características semelhantes, e mudanças nas configurações podem alterar o resultado.&lt;/p&gt;

&lt;p&gt;Também é importante lembrar que os navegadores limitam algumas informações para proteger a privacidade dos usuários.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Canvas Fingerprinting
&lt;/h3&gt;

&lt;p&gt;Outra técnica interessante é o &lt;strong&gt;Canvas Fingerprinting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nesse caso, utilizamos o elemento &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; do HTML para desenhar textos e elementos gráficos, normalmente sem exibi-los ao usuário.&lt;/p&gt;

&lt;p&gt;O detalhe é que diferentes combinações de navegador, sistema operacional, fontes e hardware gráfico podem produzir pequenas diferenças na renderização.&lt;/p&gt;

&lt;p&gt;Podemos observar isso com um exemplo simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCanvasFingerprint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;16px Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#069&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fingerprint Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getCanvasFingerprint&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O método &lt;code&gt;toDataURL()&lt;/code&gt; retorna uma representação da imagem renderizada, que pode ser utilizada como entrada para uma função de hash.&lt;/p&gt;

&lt;p&gt;Assim, pequenas diferenças na renderização podem resultar em hashes diferentes.&lt;/p&gt;

&lt;p&gt;Vale destacar que o Canvas Fingerprinting também depende de JavaScript e que diferentes dispositivos podem produzir resultados idênticos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fingerprinting é uma ameaça à segurança?
&lt;/h2&gt;

&lt;p&gt;Não necessariamente.&lt;/p&gt;

&lt;p&gt;O fingerprinting é uma técnica, e seu impacto depende da finalidade para a qual é utilizado.&lt;/p&gt;

&lt;p&gt;Em aplicações financeiras, por exemplo, pode ajudar a identificar tentativas suspeitas de acesso, detectar bots ou reconhecer padrões associados a fraudes.&lt;/p&gt;

&lt;p&gt;Por outro lado, também pode ser utilizado para rastrear usuários entre sessões e sites, mesmo quando eles removem cookies ou utilizam navegação anônima.&lt;/p&gt;

&lt;p&gt;Isso acontece porque a identificação não depende exclusivamente de informações armazenadas no navegador, mas também das características do próprio ambiente.&lt;/p&gt;

&lt;p&gt;Por esse motivo, o fingerprinting levanta discussões importantes sobre privacidade e proteção de dados, incluindo a LGPD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como se proteger do Browser Fingerprinting?
&lt;/h2&gt;

&lt;p&gt;Uma das formas de limitar a coleta de informações é &lt;strong&gt;desabilitar o JavaScript&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Como muitas técnicas de fingerprinting dependem da execução de scripts, essa medida pode impedir a coleta de diversos sinais, incluindo os utilizados no Canvas Fingerprinting.&lt;/p&gt;

&lt;p&gt;Mas existe um trade-off.&lt;/p&gt;

&lt;p&gt;Grande parte das aplicações modernas depende de JavaScript para funcionar corretamente. Ao desabilitá-lo, podemos perder funcionalidades, comprometer interfaces ou até impedir o funcionamento de determinados sites.&lt;/p&gt;

&lt;p&gt;Existem alternativas menos restritivas, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilizar navegadores com proteção contra rastreamento e fingerprinting.&lt;/li&gt;
&lt;li&gt;Manter o navegador atualizado.&lt;/li&gt;
&lt;li&gt;Evitar extensões desnecessárias, que podem aumentar a superfície de identificação.&lt;/li&gt;
&lt;li&gt;Revisar as configurações de privacidade do navegador.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nenhuma dessas medidas garante anonimato completo.&lt;/p&gt;

&lt;p&gt;Além disso, desabilitar JavaScript não impede a coleta de informações transmitidas nas requisições HTTP ou de características da conexão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerações finais
&lt;/h2&gt;

&lt;p&gt;O Browser Fingerprinting mostra como informações aparentemente simples podem ganhar outro significado quando combinadas.&lt;/p&gt;

&lt;p&gt;Para desenvolvedores, pode ser uma ferramenta interessante na construção de mecanismos de segurança e prevenção de fraudes.&lt;/p&gt;

&lt;p&gt;Para usuários, representa mais um motivo para entender quais informações são expostas durante a navegação.&lt;/p&gt;

&lt;p&gt;E aqui temos um trade-off importante: &lt;strong&gt;quanto mais informações uma aplicação coleta para reconhecer um ambiente, maiores podem ser os impactos sobre a privacidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O desafio não é apenas identificar dispositivos, mas entender quando essa identificação é realmente necessária e quais limites devemos estabelecer.&lt;/p&gt;

&lt;p&gt;No desenvolvimento de software, segurança e privacidade precisam caminhar juntas.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>webdev</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Trade-offs: por que toda decisão de arquitetura tem um preço?</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Tue, 20 Oct 2026 00:52:45 +0000</pubDate>
      <link>https://dev.to/brunomestres/trade-offs-por-que-toda-decisao-de-arquitetura-tem-um-preco-282m</link>
      <guid>https://dev.to/brunomestres/trade-offs-por-que-toda-decisao-de-arquitetura-tem-um-preco-282m</guid>
      <description>&lt;p&gt;Em arquitetura de software, não existe solução perfeita. Existe solução adequada ao contexto.&lt;/p&gt;

&lt;p&gt;Vivemos em um cenário onde novas tecnologias, ferramentas e conceitos surgem constantemente.&lt;/p&gt;

&lt;p&gt;Hoje, podemos encontrar facilmente dez soluções diferentes para resolver um mesmo problema.&lt;/p&gt;

&lt;p&gt;Mas ter mais opções não significa necessariamente tomar decisões melhores.&lt;/p&gt;

&lt;p&gt;Cada abordagem oferece vantagens, mas também apresenta custos e limitações. Por isso, precisamos avaliar cuidadosamente o que realmente faz sentido para o nosso projeto.&lt;/p&gt;

&lt;p&gt;É justamente aqui que entra um conceito fundamental na arquitetura de software: trade-off.&lt;/p&gt;

&lt;p&gt;🤔 O que é um trade-off?&lt;/p&gt;

&lt;p&gt;Um trade-off acontece quando precisamos abrir mão de algo para obter outro benefício.&lt;/p&gt;

&lt;p&gt;Imagine que você está decidindo entre uma arquitetura monolítica e microsserviços.&lt;/p&gt;

&lt;p&gt;Monolito:&lt;/p&gt;

&lt;p&gt;✅ Menor complexidade inicial.&lt;/p&gt;

&lt;p&gt;✅ Desenvolvimento e deploy mais simples.&lt;/p&gt;

&lt;p&gt;⚠️ Pode dificultar a evolução independente de partes do sistema.&lt;/p&gt;

&lt;p&gt;Microsserviços:&lt;/p&gt;

&lt;p&gt;✅ Escalabilidade independente dos serviços.&lt;/p&gt;

&lt;p&gt;✅ Maior autonomia entre equipes.&lt;/p&gt;

&lt;p&gt;⚠️ Aumento da complexidade operacional.&lt;/p&gt;

&lt;p&gt;⚠️ Maior dificuldade para lidar com comunicação e consistência de dados.&lt;/p&gt;

&lt;p&gt;Perceba que nenhuma das abordagens é necessariamente melhor.&lt;/p&gt;

&lt;p&gt;Tudo depende do problema que estamos tentando resolver.&lt;/p&gt;

&lt;p&gt;🏗️ Por que isso importa para um arquiteto de software?&lt;/p&gt;

&lt;p&gt;O papel de um arquiteto não é simplesmente escolher a tecnologia mais moderna ou o padrão mais popular.&lt;/p&gt;

&lt;p&gt;É entender os impactos de cada decisão.&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;/p&gt;

&lt;p&gt;Precisamos realmente de escalabilidade independente?&lt;/p&gt;

&lt;p&gt;Nossa equipe consegue lidar com a complexidade operacional?&lt;/p&gt;

&lt;p&gt;Estamos priorizando performance, disponibilidade ou simplicidade?&lt;/p&gt;

&lt;p&gt;O custo de manutenção dessa solução compensa seus benefícios?&lt;/p&gt;

&lt;p&gt;Essas perguntas ajudam a evitar decisões baseadas apenas em tendências.&lt;/p&gt;

&lt;p&gt;⚖️ Arquitetura é sobre escolhas&lt;/p&gt;

&lt;p&gt;Um sistema altamente distribuído pode oferecer maior flexibilidade, mas também exigir mais infraestrutura, monitoramento e conhecimento técnico.&lt;/p&gt;

&lt;p&gt;Por outro lado, uma arquitetura mais simples pode atender perfeitamente às necessidades do negócio.&lt;/p&gt;

&lt;p&gt;E está tudo bem.&lt;/p&gt;

&lt;p&gt;Complexidade não é sinônimo de qualidade arquitetural.&lt;/p&gt;

&lt;p&gt;💡 Conclusão&lt;/p&gt;

&lt;p&gt;Entender trade-offs é uma das habilidades mais importantes para quem trabalha com arquitetura de software.&lt;/p&gt;

&lt;p&gt;Porque uma boa decisão arquitetural não é aquela que oferece mais benefícios isoladamente.&lt;/p&gt;

&lt;p&gt;É aquela em que os benefícios justificam os custos e as limitações dentro do contexto do projeto.&lt;/p&gt;

&lt;p&gt;No fim, arquitetura de software não é sobre encontrar a solução perfeita.&lt;/p&gt;

&lt;p&gt;É sobre tomar decisões conscientes e saber explicar por que elas fazem sentido.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Content Security Policy (CSP) no NestJS: protegendo sua aplicação com Helmet</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Fri, 16 Oct 2026 17:36:06 +0000</pubDate>
      <link>https://dev.to/brunomestres/content-security-policy-csp-no-nestjs-protegendo-sua-aplicacao-com-helmet-24of</link>
      <guid>https://dev.to/brunomestres/content-security-policy-csp-no-nestjs-protegendo-sua-aplicacao-com-helmet-24of</guid>
      <description>&lt;p&gt;Quando falamos sobre segurança em APIs, normalmente pensamos em autenticação, autorização e validação de dados.&lt;/p&gt;

&lt;p&gt;Mas existe outra camada de proteção que muitas vezes passa despercebida: os &lt;strong&gt;headers de segurança HTTP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um deles é o &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Neste tutorial, vamos entender o que é CSP e como configurá-lo em uma aplicação NestJS utilizando o Helmet.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é CSP?
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Content Security Policy&lt;/strong&gt; é um mecanismo de segurança que permite definir quais recursos o navegador está autorizado a carregar e executar.&lt;/p&gt;

&lt;p&gt;Por exemplo, podemos restringir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scripts JavaScript.&lt;/li&gt;
&lt;li&gt;Arquivos CSS.&lt;/li&gt;
&lt;li&gt;Imagens.&lt;/li&gt;
&lt;li&gt;Recursos de domínios externos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso ajuda a reduzir os riscos de ataques como &lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt;, limitando a execução de conteúdos não autorizados.&lt;/p&gt;

&lt;p&gt;Vale destacar que o CSP é uma camada adicional de proteção, não um substituto para validação de entradas e tratamento seguro de dados.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Instalando o Helmet
&lt;/h2&gt;

&lt;p&gt;O NestJS permite utilizar o Helmet como middleware para configurar diversos headers de segurança.&lt;/p&gt;

&lt;p&gt;Primeiro, instale a dependência:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;helmet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, importe o Helmet no arquivo &lt;code&gt;main.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;helmet&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;helmet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Configurando o CSP
&lt;/h2&gt;

&lt;p&gt;Vamos adicionar a configuração ao bootstrap da aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NestFactory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;helmet&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;helmet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NestFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;styleSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'unsafe-inline'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;crossOriginEmbedderPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;hsts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31536000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;includeSubDomains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, o Helmet passa a enviar headers de segurança nas respostas HTTP da aplicação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importante:&lt;/strong&gt; o CSP é aplicado pelo navegador aos documentos que ele carrega. Em uma API que retorna apenas JSON, seu efeito direto costuma ser limitado.&lt;/p&gt;

&lt;p&gt;Ele se torna especialmente relevante quando o NestJS também entrega páginas HTML, documentação Swagger ou outros recursos consumidos pelo navegador.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Entendendo as diretivas
&lt;/h2&gt;

&lt;p&gt;Vamos analisar as configurações que utilizamos.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;defaultSrc: ["'self'"]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Define a política padrão para recursos que não possuem uma diretiva específica.&lt;/p&gt;

&lt;p&gt;O valor &lt;code&gt;'self'&lt;/code&gt; permite carregar recursos da mesma origem da página.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;scriptSrc: ["'self'"]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Restringe o carregamento de scripts à própria origem.&lt;/p&gt;

&lt;p&gt;Isso ajuda a impedir a execução de scripts externos não autorizados.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;styleSrc: ["'self'", "'unsafe-inline'"]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Permite carregar estilos da própria origem e utilizar estilos inline.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;unsafe-inline&lt;/code&gt; facilita a compatibilidade com aplicações que dependem desse comportamento, mas &lt;strong&gt;reduz a proteção oferecida pelo CSP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sempre que possível, prefira estilos externos ou mecanismos como nonces e hashes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;imgSrc: ["'self'", "data:", "https:"]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Permite carregar imagens de três tipos de origem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'self'&lt;/code&gt;: mesma origem da aplicação.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data:&lt;/code&gt;: imagens incorporadas utilizando Data URLs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https:&lt;/code&gt;: imagens provenientes de qualquer origem HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa configuração oferece flexibilidade, mas pode ser restringida a domínios específicos quando necessário.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. E as outras configurações do Helmet?
&lt;/h2&gt;

&lt;p&gt;Além do CSP, adicionamos duas configurações importantes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;crossOriginEmbedderPolicy: false&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Desativa o envio do header &lt;code&gt;Cross-Origin-Embedder-Policy&lt;/code&gt; pelo Helmet.&lt;/p&gt;

&lt;p&gt;Isso pode evitar problemas de compatibilidade com recursos externos que não estão preparados para as exigências dessa política.&lt;/p&gt;

&lt;p&gt;Por outro lado, desativá-lo significa abrir mão dessa camada de isolamento.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;hsts&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;hsts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31536000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;includeSubDomains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;strong&gt;HTTP Strict Transport Security (HSTS)&lt;/strong&gt; instrui navegadores a utilizar HTTPS para futuras conexões com o domínio.&lt;/p&gt;

&lt;p&gt;Nessa configuração:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;maxAge&lt;/code&gt;: mantém a política por aproximadamente um ano.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;includeSubDomains&lt;/code&gt;: estende a política aos subdomínios.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;preload&lt;/code&gt;: declara a intenção de utilizar a lista de pré-carregamento HSTS dos navegadores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Atenção:&lt;/strong&gt; configurar &lt;code&gt;preload: true&lt;/code&gt; não adiciona automaticamente seu domínio à lista de preload.&lt;/p&gt;

&lt;p&gt;Antes de utilizar essa opção em produção, é importante garantir que todos os subdomínios envolvidos suportem HTTPS corretamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Como verificar se está funcionando?
&lt;/h2&gt;

&lt;p&gt;Inicie a aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, faça uma requisição para um endpoint existente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deverá encontrar um header semelhante a este:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Helmet também pode adicionar outras diretivas padrão à política, mesmo que não estejam explicitamente declaradas no código.&lt;/p&gt;

&lt;p&gt;Outra opção é abrir o DevTools do navegador, acessar a aba &lt;strong&gt;Network&lt;/strong&gt; e inspecionar os Response Headers.&lt;/p&gt;

&lt;p&gt;Para verificar se uma política realmente bloqueia recursos, é necessário testá-la em uma página HTML que tente carregá-los.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Quais são os trade-offs?
&lt;/h2&gt;

&lt;p&gt;Apesar dos benefícios, configurar CSP exige alguns cuidados.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefícios
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redução da superfície de ataque:&lt;/strong&gt; ajuda a mitigar determinadas formas de XSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controle de recursos:&lt;/strong&gt; permite definir quais origens são autorizadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segurança adicional:&lt;/strong&gt; adiciona uma camada de proteção no navegador.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralização:&lt;/strong&gt; facilita a configuração de headers de segurança.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compatibilidade:&lt;/strong&gt; políticas restritivas podem bloquear recursos legítimos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estilos e scripts inline:&lt;/strong&gt; algumas bibliotecas podem exigir ajustes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrações externas:&lt;/strong&gt; domínios utilizados pela aplicação precisam ser considerados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manutenção:&lt;/strong&gt; novas dependências podem exigir alterações na política.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uma boa estratégia é começar utilizando &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; para identificar possíveis violações antes de aplicar bloqueios em produção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Implementar CSP com Helmet no NestJS é relativamente simples.&lt;/p&gt;

&lt;p&gt;O maior desafio não está em adicionar o middleware, mas em &lt;strong&gt;definir uma política que equilibre segurança e compatibilidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Também é importante lembrar que o Helmet oferece outras proteções além do CSP, e que headers de segurança não substituem boas práticas de desenvolvimento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Segurança não depende de uma única configuração. Ela é construída em camadas.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>nestjs</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CQRS: vale a pena separar leitura e escrita?</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Thu, 15 Oct 2026 00:05:38 +0000</pubDate>
      <link>https://dev.to/brunomestres/cqrs-vale-a-pena-separar-leitura-e-escrita-3ijk</link>
      <guid>https://dev.to/brunomestres/cqrs-vale-a-pena-separar-leitura-e-escrita-3ijk</guid>
      <description>&lt;p&gt;Uma das abordagens que mais gosto quando estou estruturando um projeto é o &lt;strong&gt;CQRS (Command Query Responsibility Segregation)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Já utilizei esse padrão em alguns projetos e gosto bastante da organização que ele proporciona.&lt;/p&gt;

&lt;p&gt;Mas será que vale a pena usar em qualquer aplicação?&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é CQRS?
&lt;/h2&gt;

&lt;p&gt;A ideia é simples: &lt;strong&gt;separar as operações de leitura das operações de escrita.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em uma aplicação tradicional, um mesmo serviço pode ser responsável por criar, atualizar, excluir e consultar dados.&lt;/p&gt;

&lt;p&gt;Com CQRS, dividimos essas responsabilidades em dois grupos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Commands:&lt;/strong&gt; operações que modificam o estado da aplicação, como criar ou atualizar um usuário.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queries:&lt;/strong&gt; operações que apenas consultam informações, como buscar ou listar usuários.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E um detalhe importante: não precisamos de microsserviços ou bancos de dados separados para utilizar CQRS.&lt;/p&gt;

&lt;p&gt;Essa separação pode acontecer dentro de um monólito.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que ganhamos? ✅
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organização:&lt;/strong&gt; responsabilidades mais claras.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manutenção:&lt;/strong&gt; facilita encontrar e modificar operações específicas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibilidade:&lt;/strong&gt; permite utilizar modelos diferentes para leitura e escrita.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalabilidade:&lt;/strong&gt; abre possibilidades para otimizar os dois fluxos independentemente.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  E o que perdemos? ❌
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mais complexidade:&lt;/strong&gt; uma operação simples pode exigir mais componentes e arquivos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mais abstrações:&lt;/strong&gt; aumenta a curva de aprendizado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mais manutenção:&lt;/strong&gt; exige consistência na organização do projeto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sincronização:&lt;/strong&gt; caso os dados sejam armazenados separadamente, podemos precisar lidar com consistência eventual.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Afinal, vale a pena?
&lt;/h2&gt;

&lt;p&gt;Particularmente, gosto bastante de CQRS pela organização que proporciona.&lt;/p&gt;

&lt;p&gt;Mas, para um CRUD simples, talvez essa separação seja mais trabalho do que benefício.&lt;/p&gt;

&lt;p&gt;Já em aplicações com regras de negócio mais complexas, pode fazer bastante sentido.&lt;/p&gt;

&lt;p&gt;No fim, &lt;strong&gt;não existe padrão arquitetural gratuito. Toda escolha traz benefícios e custos.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cqrs</category>
      <category>architecture</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Coesão e Acoplamento: dois conceitos fundamentais da arquitetura de software</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Sun, 11 Oct 2026 17:39:14 +0000</pubDate>
      <link>https://dev.to/brunomestres/coesao-e-acoplamento-dois-conceitos-fundamentais-da-arquitetura-de-software-2d59</link>
      <guid>https://dev.to/brunomestres/coesao-e-acoplamento-dois-conceitos-fundamentais-da-arquitetura-de-software-2d59</guid>
      <description>&lt;h1&gt;
  
  
  Coesão e Acoplamento: dois conceitos fundamentais da arquitetura de software
&lt;/h1&gt;

&lt;p&gt;Ao desenvolver uma aplicação, é comum focarmos em funcionalidades, regras de negócio e requisitos. Mas existe outra preocupação importante: &lt;strong&gt;como organizamos o código e como as partes do sistema se relacionam?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;É nesse ponto que dois conceitos ganham importância: coesão e acoplamento.&lt;/p&gt;

&lt;p&gt;Embora sejam conceitos simples, eles influenciam diretamente a manutenibilidade, a testabilidade e a capacidade de evolução de uma aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é coesão?
&lt;/h2&gt;

&lt;p&gt;Coesão representa o quanto as responsabilidades de um módulo estão relacionadas entre si.&lt;/p&gt;

&lt;p&gt;Um módulo com alta coesão possui um propósito bem definido. Suas funcionalidades trabalham em conjunto para cumprir uma responsabilidade específica.&lt;/p&gt;

&lt;p&gt;Imagine um serviço responsável pelo gerenciamento de usuários. Ele pode conter operações como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cadastrar um usuário.&lt;/li&gt;
&lt;li&gt;Atualizar seus dados.&lt;/li&gt;
&lt;li&gt;Consultar suas informações.&lt;/li&gt;
&lt;li&gt;Desativar uma conta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essas operações estão relacionadas ao mesmo domínio, o que contribui para uma boa coesão.&lt;/p&gt;

&lt;p&gt;Agora imagine que esse mesmo serviço também seja responsável por processar pagamentos e gerar relatórios financeiros.&lt;/p&gt;

&lt;p&gt;As responsabilidades começam a se distanciar, tornando o componente mais difícil de compreender e modificar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A ideia é manter juntas as funcionalidades que pertencem ao mesmo propósito.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é acoplamento?
&lt;/h2&gt;

&lt;p&gt;Acoplamento representa o grau de dependência entre diferentes módulos ou componentes.&lt;/p&gt;

&lt;p&gt;Quando um componente depende diretamente dos detalhes internos de outro, mudanças na implementação podem gerar impactos em diferentes partes do sistema.&lt;/p&gt;

&lt;p&gt;Considere o seguinte exemplo em TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySQLDatabase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Salvando no MySQL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MySQLDatabase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, &lt;code&gt;UserService&lt;/code&gt; cria diretamente uma instância de &lt;code&gt;MySQLDatabase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Isso significa que o serviço está acoplado a uma implementação específica. Se precisarmos trocar o banco de dados ou utilizar uma implementação em memória nos testes, será necessário modificar a estrutura ou criar mecanismos adicionais para acomodar essa mudança.&lt;/p&gt;

&lt;p&gt;Uma alternativa é depender de uma abstração:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySQLDatabase&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Salvando no MySQL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, &lt;code&gt;UserService&lt;/code&gt; não precisa conhecer a implementação concreta do banco de dados.&lt;/p&gt;

&lt;p&gt;Podemos fornecer diferentes implementações que respeitem o contrato definido pela interface.&lt;/p&gt;

&lt;p&gt;Isso reduz o acoplamento com uma implementação específica e facilita a substituição de dependências.&lt;/p&gt;

&lt;p&gt;Vale destacar que a interface, sozinha, não elimina o acoplamento. O serviço ainda depende do contrato &lt;code&gt;Database&lt;/code&gt;, mas não precisa depender de uma implementação concreta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como esses conceitos se complementam?
&lt;/h2&gt;

&lt;p&gt;Coesão e acoplamento analisam aspectos diferentes da organização do software.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alta coesão:&lt;/strong&gt; as responsabilidades de um módulo fazem sentido juntas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baixo acoplamento:&lt;/strong&gt; os módulos possuem dependências controladas e não precisam conhecer detalhes desnecessários uns dos outros.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O objetivo não é eliminar todas as dependências. Sistemas precisam de colaboração entre componentes para funcionar.&lt;/p&gt;

&lt;p&gt;O importante é organizar essa colaboração de maneira que uma mudança não provoque uma sequência desnecessária de alterações em toda a aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Isso significa que precisamos criar mais abstrações?
&lt;/h2&gt;

&lt;p&gt;Não necessariamente.&lt;/p&gt;

&lt;p&gt;Adicionar interfaces, classes e camadas sem uma necessidade concreta pode aumentar a complexidade em vez de reduzi-la.&lt;/p&gt;

&lt;p&gt;Uma aplicação pequena pode funcionar muito bem com uma estrutura simples. Conforme surgem novas responsabilidades e necessidades de evolução, determinadas abstrações podem começar a fazer sentido.&lt;/p&gt;

&lt;p&gt;A arquitetura deve resolver problemas reais, não criar estruturas apenas para seguir um padrão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Coesão e acoplamento são fundamentos importantes para construir sistemas que possam evoluir de forma sustentável.&lt;/p&gt;

&lt;p&gt;Ao organizar um módulo, pergunte se suas responsabilidades realmente pertencem juntas.&lt;/p&gt;

&lt;p&gt;Ao criar uma dependência, avalie se o componente precisa conhecer uma implementação específica ou se pode trabalhar com um contrato mais estável.&lt;/p&gt;

&lt;p&gt;Essas decisões parecem pequenas, mas podem fazer uma diferença significativa conforme a aplicação cresce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uma boa arquitetura não é aquela que elimina a complexidade, mas aquela que evita espalhá-la desnecessariamente pelo sistema.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>typescript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Thu, 27 Feb 2025 02:53:18 +0000</pubDate>
      <link>https://dev.to/brunomestres/-59d4</link>
      <guid>https://dev.to/brunomestres/-59d4</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/brunomestres" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1201765%2Fc32ea846-ebd6-45b2-bb73-f191e51a7ea4.jpeg" alt="brunomestres"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/brunomestres/como-rodar-uma-api-nodejs-postgresql-com-docker-41di" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;🚀 Como Rodar uma API Node.js + PostgreSQL com Docker&lt;/h2&gt;
      &lt;h3&gt;Bruno Mestres ・ Feb 26&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#postgres&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>docker</category>
      <category>node</category>
      <category>postgres</category>
    </item>
    <item>
      <title>🚀 Como Rodar uma API Node.js + PostgreSQL com Docker</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Wed, 26 Feb 2025 20:53:18 +0000</pubDate>
      <link>https://dev.to/brunomestres/como-rodar-uma-api-nodejs-postgresql-com-docker-41di</link>
      <guid>https://dev.to/brunomestres/como-rodar-uma-api-nodejs-postgresql-com-docker-41di</guid>
      <description>&lt;p&gt;Hoje, vou compartilhar um exemplo prático e direto para quem quer aprender Docker: subir uma API Node.js conectada ao PostgreSQL sem precisar instalar o banco localmente.&lt;/p&gt;

&lt;p&gt;Se você já perdeu tempo configurando dependências no seu ambiente ou precisa garantir que tudo funcione igual em qualquer máquina, Docker é a solução!&lt;/p&gt;

&lt;p&gt;📌 Estruturando o Projeto&lt;br&gt;
Criamos dois containers:&lt;br&gt;
🔹 App Node.js para servir uma API&lt;br&gt;
🔹 Banco PostgreSQL para armazenar os dados&lt;/p&gt;

&lt;p&gt;&lt;code&gt;📦 meu-projeto-docker&lt;br&gt;
 ┣ 📂 src&lt;br&gt;
 ┃ ┣ 📜 main.ts&lt;br&gt;
 ┣ 📜 Dockerfile&lt;br&gt;
 ┣ 📜 docker-compose.yml&lt;br&gt;
 ┣ 📜 package.json&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
1️⃣ Criando a API Node.js&lt;br&gt;
Dentro do projeto, inicialize o package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instale as dependências:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express pg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, crie src/main.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import pg from 'pg'
const { Pool } = pg
const app = express();

const pool = new Pool({
  user: "postgres",
  host: "db",
  database: "meubanco",
  password: "postgres",
  port: 5432,
});

app.get("/", async (req, res) =&amp;gt; {
  const response = await pool.query("SELECT NOW()");

  res.send(`Banco conectado! Hora atual: ${response.rows[0].now}`);
});



app.listen(3000, () =&amp;gt; console.log("API running on port 3000"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2️⃣ Criando o Dockerfile&lt;/p&gt;

&lt;p&gt;Esse arquivo define a imagem da nossa API:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:23
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "src/main.ts"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3️⃣ Configurando o Docker Compose&lt;/p&gt;

&lt;p&gt;Aqui criamos os serviços da API e do PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.8"

services:
  db:
    image: postgres:15
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: meubanco
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  app:
    build: .
    restart: always
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/app

volumes:
  pgdata:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4️⃣ Rodando o Projeto 🚀&lt;/p&gt;

&lt;p&gt;Agora é só rodar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E acessar a API em:&lt;br&gt;
&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enfim, com Docker, garantimos que nosso ambiente seja consistente, evitando aqueles famosos "na minha máquina funciona".&lt;/p&gt;

&lt;p&gt;Esse foi só o começo! Podemos evoluir esse setup com migrations, CI/CD e Kubernetes. Se quiser mais conteúdos como esse, comenta aí! 👇🔥&lt;/p&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Construindo uma API segura e eficiente com @fastify/jwt e @fastify/mongodb</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Mon, 13 Jan 2025 22:14:10 +0000</pubDate>
      <link>https://dev.to/brunomestres/construindo-uma-api-segura-e-eficiente-com-fastifyjwt-e-fastifymongodb-1fck</link>
      <guid>https://dev.to/brunomestres/construindo-uma-api-segura-e-eficiente-com-fastifyjwt-e-fastifymongodb-1fck</guid>
      <description>&lt;p&gt;Ultimamente tenho utilizado bastante o Fastify em meus projetos pessoais, recentemente descobri diversas funcionalidades através de plugins oficiais.&lt;/p&gt;

&lt;p&gt;Eles são leves, eficientes e, o melhor de tudo, encaixam perfeitamente no ecossistema do Fastify.&lt;/p&gt;

&lt;p&gt;Então resolvi criar um projeto prático com autenticação e persistência, utilizando o JWT e o MongoDB.&lt;/p&gt;

&lt;p&gt;Plugins usados no projeto: &lt;/p&gt;

&lt;p&gt;⚒️ O @fastify/jwt é um plugin oficial que simplifica a autenticação com JSON Web Tokens. Com ele, dá pra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gerar tokens JWT de forma rápida.&lt;/li&gt;
&lt;li&gt;Verificar tokens automaticamente nas rotas protegidas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚒️ @fastify/mongodb: Plugin oficial do Fastify para integração com o banco de dados MongoDB.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ele se encarrega de gerenciar a conexão com o MongoDB de forma eficiente, sem que eu precise ficar configurando drivers manualmente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Os plugins seguem uma interface padrão. Basta registrá-los com fastify.register(), definir algumas configurações e pronto.&lt;/p&gt;

&lt;p&gt;Depois disso posso, acessar o banco diretamente com fastify.mongo e o módulo do jwt com o fastify.jwt.&lt;/p&gt;

&lt;p&gt;Também criei um decorator para anexar apenas as rotas que seriam protegidas, onde ele verificará o token passado no cabeçalho.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Fastify from "fastify";
import mongodb from "@fastify/mongodb";
import jwt from "@fastify/jwt";
import { config } from "./constants.ts";
import * as bcrypt from "bcrypt";

const fastify = Fastify();

type RequestUser = {
  username: string;
  email: string;
  password: string;
};

fastify.register(mongodb, {
  forceClose: true,
  url: config.MONGO_URL,
});

fastify.register(jwt, {
  secret: "supersecret",
});

fastify.post("/signup", async (req, reply) =&amp;gt; {
  const salt = 10;
  const { email, password, username } = req.body as RequestUser;

  const data = {
    username,
    email,
    password: await bcrypt.hash(password, salt),
  };

  const user = await fastify.mongo.client.db("plugins").collection("users").insertOne(data);

  reply.send({ user })
});

fastify.decorate("auth", async (request, reply) =&amp;gt; {
  try {
    await request.jwtVerify();
  } catch (err) {
    reply.send(err);
  }
});

fastify.post("/signin", (req, reply) =&amp;gt; {
  // some code
  const body = req.body;
  const token = fastify.jwt.sign({ body });
  console.log(token);
  reply.send({ token });
});

fastify.get("/", { onRequest: [fastify.auth] }, async (req, reply) =&amp;gt; {
  const users = fastify.mongo?.client.db("plugins").collection("users").find();

  const data: any = [];

  for await (const doc of users) {
    data.push(doc);
  }
  return data;
});

fastify.listen({ port: 3333 }, (err) =&amp;gt; {
  if (err) process.exit(1);

  console.log("Running server");
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>backend</category>
      <category>api</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Design Pattern - Strategy</title>
      <dc:creator>Bruno Mestres</dc:creator>
      <pubDate>Mon, 18 Mar 2024 13:52:30 +0000</pubDate>
      <link>https://dev.to/brunomestres/design-pattern-strategy-4dff</link>
      <guid>https://dev.to/brunomestres/design-pattern-strategy-4dff</guid>
      <description>&lt;p&gt;Ultimamente tenho estudado bastante padrões de projetos, então resolvi escrever um pouco sobre alguns, e decidi começar pelo Strategy.&lt;/p&gt;

&lt;p&gt;O Strategy é um padrão de projeto da categoria comportamental, que permite que você crie uma hierarquia de algoritmos, separando os objetos intercambiáveis em classes separadas.&lt;br&gt;
Esse padrão é muito útil quando temos um código carregado de condições, ele permite que nosso código se adapte a diversas situações que venha acontecer posteriormente. &lt;/p&gt;

&lt;p&gt;Digamos que tenham algumas regras de negocio para dar desconto em um Ecommerce, e ela varia dependendo de diversos fatores, como quantidade de produtos no carrinho, ou um determinado valor a ser batido, promoções e etc. Nesse caso a classe que representa o carrinho de compra teria que verificar cada uma dessas variantes com diversos IFS e aplicar o valor correto, e a medida que mais opções de desconto fosse adicionado na regra de negocio, teríamos que mexer na classe do carrinho, tornando o código cada vez mais difícil de se manter e aumentando a chance de criar bugs em códigos que já estavam funcionando.&lt;/p&gt;

&lt;p&gt;O Strategy sugere que nesse caso, pegue a classe que tem diversos IFS e separe os códigos em outras classes separadas, chamadas de estratégias. &lt;/p&gt;

&lt;p&gt;Na estrutura desse Padrão temos:&lt;br&gt;
    O contexto e a classe que contem o algoritmo com a lógica comum, ela vem carregada de condições.&lt;br&gt;
    A interface da Strategy, que define a função que as estratégias deverão executar.&lt;br&gt;
    E as estratégias que são os algoritmos específicos para cada situação que o código cliente chamar. &lt;/p&gt;

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

</description>
      <category>designpatterns</category>
      <category>strategy</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
