<?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: Marcelo Albuquerque</title>
    <description>The latest articles on DEV Community by Marcelo Albuquerque (@marcelo-albuquerque).</description>
    <link>https://dev.to/marcelo-albuquerque</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F587998%2Fea577366-ec6c-4758-bf8f-ae52faba6310.jpeg</url>
      <title>DEV Community: Marcelo Albuquerque</title>
      <link>https://dev.to/marcelo-albuquerque</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcelo-albuquerque"/>
    <language>en</language>
    <item>
      <title>Utilizando Pool de conexões com MySQL2 em uma aplicação Next.js</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Sun, 12 Nov 2023 20:26:42 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/utilizando-pool-de-conexoes-com-mysql2-em-uma-aplicacao-nextjs-4k38</link>
      <guid>https://dev.to/marcelo-albuquerque/utilizando-pool-de-conexoes-com-mysql2-em-uma-aplicacao-nextjs-4k38</guid>
      <description>&lt;p&gt;Uma breve análise da utilização do Pool de conexões do MySQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerações
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next.js com a &lt;a href="https://nextjs.org/docs/app"&gt;App Router&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Banco de dados MySQL utilizando &lt;a href="https://aws.amazon.com/pt/rds/mysql/"&gt;Amazon RDS for MySQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pacote &lt;a href="https://www.npmjs.com/package/mysql2"&gt;MySQL2&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Explicando o Problema
&lt;/h2&gt;

&lt;p&gt;Trabalhando em uma aplicação comecei a receber uma mensagem de erro muito incomoda: &lt;code&gt;Too many connections&lt;/code&gt;, o código &lt;code&gt;errno&lt;/code&gt; é o &lt;code&gt;1040&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Bem, obviamente havia algo errado. Antes de analisar o código da função para conexão ao bando de dados, observei que minha aplicação realizava muitas chamadas desnecessárias, então passei a guardar algumas informações em &lt;code&gt;states&lt;/code&gt; e trabalhei na otimização geral do código da aplicação.&lt;/p&gt;

&lt;p&gt;Essa etapa foi muito importante, pois não podemos simplesmente sair disparando chamadas para o banco de dados de forma irresponsável.&lt;/p&gt;

&lt;p&gt;Então enfim cheguei no código da função que realiza a conexão com o banco de dados e comecei a verificar - antes de realizar qualquer mudança - a quantidade conexões ao banco de dados toda vez que carregava a aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;show&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nv"&gt;`variable_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Threads_connected'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que observei foi um número crescente de conexões, que invariavelmente poderia chegar ao limite do meu banco de dados na AWS, utilizo o &lt;a href="https://aws.amazon.com/pt/rds/mysql/"&gt;Amazon RDS for MySQL&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analisando a implementação atual
&lt;/h2&gt;

&lt;p&gt;Agora observe o meu código de conexão ao banco de dados:&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="c1"&gt;// libs/mysql/index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mysql&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;mysql2/promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/*
  Com essa implementação, o número de conexões precisa ser muito controlado.
*/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MySQL&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;connection&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;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createConnection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_PASSWORD&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;connection&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O detalhe principal é que estou utilizando o método &lt;code&gt;createConnection&lt;/code&gt;. E esta implementação precisa ser cuidadosamente estudada para ser implementada, pois toda chamada a ela irá gerar uma nova conexão ao banco de dados e claramente atingir o limite passa a ser apenas uma questão de tempo ou simplesmente de escalabilidade da aplicação.&lt;/p&gt;

&lt;p&gt;Então comecei a ir atrás de soluções, estudar um pouco mais sobre implementações de conexão ao banco de dados MySQL.&lt;/p&gt;

&lt;p&gt;Já havia ouvido sobre Pool de Conexões, mas nunca dei a devida atenção.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/sql/docs/mysql/manage-connections?hl=pt-br#pools"&gt;Um pool de conexões é um cache de conexões de banco de dados que são compartilhadas e reutilizadas para melhorar a latência e o desempenho da conexão. Quando seu aplicativo precisa de uma conexão de banco de dados, ele pega uma emprestada do pool temporariamente. Assim que ele termina de usar a conexão, ela é devolvida ao pool para ser reutilizada da próxima vez em que o aplicativo precisar de uma conexão de banco de dados.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Aplicando a solução
&lt;/h2&gt;

&lt;p&gt;Ótimo, conceitualmente é fantástico. A biblioteca que utilizo: &lt;a href="https://www.npmjs.com/package/mysql2#using-connection-pools"&gt;MySQL 2&lt;/a&gt; possuí este recurso, então bastava implementar e realizar os testes:&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="c1"&gt;// /libs/mysql/index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mysql&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;mysql2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MySQL&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;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_PASSWORD&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;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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;pool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe que agora estou utilizando o método &lt;code&gt;createPool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Estou exportando uma função assíncrona, observe que a &lt;code&gt;connection&lt;/code&gt; gera uma &lt;code&gt;promise&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vou deixar aqui apenas um exemplo de como utilizar esse Pool de conexões para gerar uma consulta no banco de dados:&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="c1"&gt;// /api/consulta/route.ts&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;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MySQL&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;/libs/mysql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;mysql&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;MySQL&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM produtos`&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;]&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;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Extremamente importante. Encerrar a conexão.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&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;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;error&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="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;error&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;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Ao passar a utilizar o Pool de conexões observei que o número de conexões ao banco de dados diminuiu drasticamente, utilizando basicamente apenas uma única conexão.&lt;/p&gt;

&lt;p&gt;Algo extremamente importante nessa implementação é o encerramento da conexão: &lt;code&gt;await mysql.end()&lt;/code&gt; principalmente utilizando funções assíncronas, caso contrário, o resultado seria o mesmo de utilizar o método &lt;code&gt;createConnection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Infelizmente não dediquei o devido tempo ao estudo do MySQL, com certeza teria percebido esse problemas antes, mas deixo aqui para aqueles que de alguma forma possam estar enfrentando problemas semelhantes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comandos SQL que podem ser úteis
&lt;/h3&gt;

&lt;p&gt;Exibir a quantidade de conexões ao banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;`variable_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Threads_connected'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para analisar a proveniência das conexões:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;processlist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exibe o número máximo de conexões que seu banco de dados suporta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;VARIABLES&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="nv"&gt;"max_connections"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Referências
&lt;/h3&gt;

&lt;p&gt;Também quero destacar uma postagem no &lt;a href="https://blog.devgenius.io/nodejs-how-to-close-your-mysql-connections-and-why-a7cc7287132b"&gt;Medium&lt;/a&gt; que me foi de grande ajudar, até considero que essa postagem deveria fazer parte da documentação oficial do MySQL 2. O autor se chamar &lt;a href="https://medium.com/@jorgen_90767"&gt;Jorgen Lundgren&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Também obtive ajuda em uma pergunta no &lt;a href="https://stackoverflow.com/questions/7432241/mysql-show-status-active-or-total-connections"&gt;Stack Overflow&lt;/a&gt; onde nas respostas os usuários citaram diversos comandos úteis para o MySQL.&lt;/p&gt;

</description>
      <category>mysql2</category>
      <category>pool</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Implementando Editor.js com Next.js utilizando Typescript</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Sun, 05 Nov 2023 22:17:37 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/implementando-editorjs-com-nextjs-utilizando-typescript-2j5g</link>
      <guid>https://dev.to/marcelo-albuquerque/implementando-editorjs-com-nextjs-utilizando-typescript-2j5g</guid>
      <description>&lt;p&gt;Basicamente o Editor.js é um WYSIWYG editor. Tenho certeza que irá se surpreender com essa solução e ficar ainda mais impressionado sabendo que é gratuíta. E ao meu ver está muito a frente de outras soluções pagas.&lt;/p&gt;

&lt;p&gt;Conheça o &lt;a href="https://editorjs.io/"&gt;Editor.js&lt;/a&gt;. E também considere fazer uma &lt;a href="https://opencollective.com/editorjs"&gt;doação&lt;/a&gt; para o projeto. São poucos projetos gratuítos com esse nível de qualidade.&lt;/p&gt;

&lt;p&gt;A proposta deste tutorial é mostrar sua implementação com o &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; utilizando Typescript.&lt;/p&gt;

&lt;p&gt;Vamos utilizar o &lt;a href="https://nextjs.org/docs/app"&gt;App Router&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando o componente Editor
&lt;/h2&gt;

&lt;p&gt;Primeiramente vamos construir um componente que irá exportar uma &lt;code&gt;div&lt;/code&gt; onde o Editor.js será renderizado após a tela estar pronta para o usuário.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Editor.tsx&lt;/span&gt;

&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&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;useRef&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;react&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;EditorJS&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;@editorjs/editorjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Esta função irá garantir que o componente seja renderizado uma única vez&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RenderEditor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;ElementId&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="o"&gt;=&amp;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;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="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;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Aqui chamamos a execução do EditorJS e também podemos passar os parâmetros necessários para execução&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EditorJS&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;holder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ElementId&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Editor&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;elementId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editorjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Defina aqui o ID para o elemento onde o Editor.js será renderizado&lt;/span&gt;

  &lt;span class="nx"&gt;RenderEditor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;elementId&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;elementId&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;h2&gt;
  
  
  Importando o Componente
&lt;/h2&gt;

&lt;p&gt;Para importar o componente vamos utilizar o &lt;a href="https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#nextdynamic"&gt;&lt;code&gt;next/dynamic&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Essa implementação se faz necessária para evitar erros na execução do Editor.js, lembrando que oficialmente não existe um componente React, mas podemos construir nosso próprio componente e utilizar sem nenhum problema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// page.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/dynamic&lt;/span&gt;&lt;span class="dl"&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;Editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Editor&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="na"&gt;ssr&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;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading ...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Page&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Editor&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;Com essa implementação o Editor.js deverá ser executado sem erros no seu Fron-end.&lt;/p&gt;

&lt;p&gt;Gostaria de destacar uma resposta no &lt;a href="https://stackoverflow.com/a/72953313/10265910"&gt;Stackoverflow&lt;/a&gt; que me auxiliou na construção da função que garante que o Editor.js será renderizado uma única vez.&lt;/p&gt;

&lt;p&gt;Até mais!!!&lt;/p&gt;

</description>
      <category>editorjs</category>
      <category>nextjs</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Como exportar base de dados utilizando MySQLWorkbench para AWS RDS</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Fri, 03 Nov 2023 19:17:53 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/como-exportar-base-de-dados-utilizando-mysqlworkbench-para-aws-rds-3ah8</link>
      <guid>https://dev.to/marcelo-albuquerque/como-exportar-base-de-dados-utilizando-mysqlworkbench-para-aws-rds-3ah8</guid>
      <description>&lt;p&gt;Após realizar o &lt;code&gt;export&lt;/code&gt; de uma base de dados que possuo na AWS RDS e tentar importá-la recebi a seguinte mensagem de erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access denied; you need (at least one of) the SUPER privilege(s) for this operation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O problema está no processo de exportação, devemos desativar uma diretiva que por padrão está em &lt;code&gt;AUTO&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;set-gtid-purged - Add 'SET @@GLOBAL.GTID_PURGED' to the output.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No painel de exportação, no canto superior direito, encontramos a opção: &lt;code&gt;Advanced Options...&lt;/code&gt;. Ao acessá-la devemos alterar o valor da diretiva para &lt;code&gt;OFF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hhrsrUvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38rkqylpmzxw0hdn50ds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hhrsrUvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38rkqylpmzxw0hdn50ds.png" alt="Image description" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysqlworkbench</category>
      <category>aws</category>
      <category>rds</category>
    </item>
    <item>
      <title>Resolvendo problema ao realizar migração de dados utilizando a opção Migration Wizard no MySQLWorkbench no MacOS</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Fri, 03 Nov 2023 19:00:41 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/resolvendo-problema-ao-realizar-migracao-de-dados-utilizando-a-opcao-migration-wizard-no-mysqlworkbench-no-macos-5hb9</link>
      <guid>https://dev.to/marcelo-albuquerque/resolvendo-problema-ao-realizar-migracao-de-dados-utilizando-a-opcao-migration-wizard-no-mysqlworkbench-no-macos-5hb9</guid>
      <description>&lt;p&gt;Ao iniciar o processo de migração tudo transcorre normalmente até chegar ao processo "STEP BULK DATA TRANSFER" onde recebemos o seguinte erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dyld: Library not loaded: @executable_path/../Frameworks/libmysqlclient.dylib
  Referenced from: /Applications/MySQLWorkbench.app/Contents/MacOS/wbcopytables
  Reason: no suitable image found.  Did find:
    file system relative paths not allowed in hardened programs

ERROR: Determine number of rows to copy: Error getting row count from source tables, wbcopytables exited with code -6
Failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse problema ocorre pois a instalação do MySQLWorkbench não cria o arquivo com o nome &lt;code&gt;libmysqlclient.dylib&lt;/code&gt;, no lugar, a instalação cria outro arquivo com um nome semelhante: &lt;code&gt;libmysqlclient.21.dylib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para resolver o problema, podemos simplesmente realizar uma cópia do arquivo &lt;code&gt;libmysqlclient.21.dylib&lt;/code&gt; e renomeá-lo para &lt;code&gt;libmysqlclient.dylib&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Podemos acessar o diretório &lt;code&gt;Applications&lt;/code&gt; onde se localizam as aplicações instaladas e ao localizar o &lt;code&gt;MySQLWorkbench&lt;/code&gt; devemos clicar com o botão direito do mouse e escolher a opção &lt;code&gt;Show Package Content&lt;/code&gt;, assim podemos navegar dentro do pacote de instalação. Em seguida acessamos a pasta &lt;code&gt;Content &amp;gt; Frameworks&lt;/code&gt; onde se localiza o arquivo &lt;code&gt;libmysqlclient.21.dylib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Basta realizar a cópia e renomeação do arquivo para tudo funcionar normalmente.&lt;/p&gt;

</description>
      <category>mysqlworkbench</category>
      <category>libmysqlclient</category>
      <category>macos</category>
    </item>
    <item>
      <title>Auth0 getSession() com Typescript no Next.js</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Wed, 25 Oct 2023 02:07:15 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/auth0-getsession-com-typescript-no-nextjs-3d7n</link>
      <guid>https://dev.to/marcelo-albuquerque/auth0-getsession-com-typescript-no-nextjs-3d7n</guid>
      <description>&lt;p&gt;Para obter as informações do usuário logado em &lt;strong&gt;server-side&lt;/strong&gt; o Auth0 dispõe da função &lt;code&gt;getSession()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A função &lt;code&gt;getSession()&lt;/code&gt; não retorna diretamente o objeto contendo as informações do usuário, primeiramente é preciso acessar o objeto &lt;code&gt;user&lt;/code&gt; dentro de &lt;code&gt;getSession()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Em Typescript isso pode trazer alguns problemas, pois esse objeto não está devidamente tipado.&lt;/p&gt;

&lt;p&gt;A documentação do Auth0 nos orienta a desestruturar o objeto &lt;code&gt;user&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getSession&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;@auth0/nextjs-auth0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ProfileClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Typescript Error: Property 'user' does not exist on type 'Session | null | undefined'.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&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;getSession&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;Observe que a melhor forma, realmente seria a de desestruturar o objeto, em JavaScript nenhum erro será retornado, porém, em TypeScript o erro é claro.&lt;/p&gt;

&lt;p&gt;Adotei a seguinte abordagem: Criei uma tipagem customizada idêntica a utilizada ao obter as informações do usuário em &lt;strong&gt;client-side&lt;/strong&gt;. &lt;em&gt;No lado do cliente podemos utilizar a função &lt;code&gt;useUser()&lt;/code&gt; e desestruturar o objeto &lt;code&gt;user&lt;/code&gt;, aqui a tipagem funciona perfeitamente, portanto, apenas copiei a tipagem correta.&lt;/em&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getSession&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;@auth0/nextjs-auth0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Tipagem customizada idêntica a função `useUser()` do client-side&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;userSessionProfileTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&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="nl"&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="nl"&gt;nickname&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="nl"&gt;picture&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="nl"&gt;sub&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="nl"&gt;updated_at&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="nl"&gt;org_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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ProfileClient&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;userSession&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;getSession&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="nx"&gt;userSession&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;userSession&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Aqui podemos utilizar a tipagem customizada&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;userSessionProfileTypes&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;Dessa forma podemos utilizar o objeto &lt;code&gt;user&lt;/code&gt; e acessar facilmente suas chaves.&lt;/p&gt;

</description>
      <category>auth0</category>
      <category>typescript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>NextAuth - Implementando "Custom Login Pages" com "Credentials Sign in" utilizando App Router</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Sun, 15 Oct 2023 17:11:55 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/nextauth-implementando-custom-login-pages-com-credentials-sign-in-utilizando-app-router-3i5d</link>
      <guid>https://dev.to/marcelo-albuquerque/nextauth-implementando-custom-login-pages-com-credentials-sign-in-utilizando-app-router-3i5d</guid>
      <description>&lt;p&gt;Quero abordar um assunto muito específico relacionado a implementação de uma página de login personalizada com Credentials Sign in utilizando &lt;a href="https://next-auth.js.org/"&gt;NextAuth&lt;/a&gt; com a estrutura &lt;a href="https://nextjs.org/docs/app/building-your-application"&gt;App Router&lt;/a&gt; do Next.js.&lt;/p&gt;

&lt;p&gt;Algo que ficou muito claro para mim é que o NextAuth foi construído para funcionar com a estrutura &lt;a href="https://nextjs.org/docs/pages/building-your-application"&gt;Pages Router&lt;/a&gt; do Next.js e sua compatibilidade com a App Router definitivamente não é a ideal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo a lógica de funcionamento
&lt;/h2&gt;

&lt;p&gt;Ao optar por utilizar uma página de login personalizada é preciso ficar atendo aos requisitos para tal. Afinal, aqui estamos optando por personalizar a experiência de Login de nossos usuários, o que de fato pode ser algo extremamente positivo, porém, também pode ser um tanto trabalhoso.&lt;/p&gt;

&lt;p&gt;Quero focar na implementação do login com &lt;a href="https://next-auth.js.org/configuration/pages#credentials-sign-in"&gt;credenciais&lt;/a&gt;, onde defino uma lógica personalizada para verificar o usuário.&lt;/p&gt;

&lt;p&gt;Vamos pensar no cenário onde necessitamos validar o nome e senha do usuário. A documentação do NextAuth nos orienta a criar um formulário relativamente simples, porém, com uma informação crucial: csrfToken.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/api/auth/callback/credentials"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    // Este campo recebe o csrfToken e é obrigatório, porém, é um campo oculto.
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"csrfToken"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;defaultValue=&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt; &lt;span class="na"&gt;csrfToken&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Digite seu Email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Digite sua Senha"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Entrar&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;Aqui reside o maior problema! Como obter o csrfToken? Bom, o NextAuth nos oferece uma função para obtê-lo: &lt;code&gt;getCsrfToken()&lt;/code&gt;, conforme podemos ver na &lt;a href="https://next-auth.js.org/getting-started/client#getcsrftoken"&gt;documentação&lt;/a&gt;. Essa função funciona muito bem na Pages Router do Next.js, porém, ela simplesmente não funciona na App Router. Não funciona pois ela retorna um token diferente do realmente definido pela aplicação.&lt;/p&gt;

&lt;p&gt;Esse token é armazenado como um cookie em nosso navegador e é muito fácil fazer essa comparação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como obter o CSRF Token da forma correta
&lt;/h2&gt;

&lt;p&gt;O Next.js nos oferece uma função para obter os &lt;a href="https://nextjs.org/docs/app/api-reference/functions/cookies"&gt;cookies&lt;/a&gt; do navegador.&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;cookies&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;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SignIn&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;cookieStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Observe que estou utilizando uma variável de ambiente&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csrfToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookieStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXTAUTH_CSRF_TOKEN&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&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;p&gt;A explicação do código é relativamente simples, obtenho o cookie informando o seu nome e recupero a parte dele que realmente necessito.&lt;/p&gt;

&lt;p&gt;O ponto principal aqui é que em desenvolvimento o nome do cookie é &lt;em&gt;next-auth.csrf-token&lt;/em&gt; e em produção __&lt;em&gt;Host-next-auth.csrf-token&lt;/em&gt;, por esse motivo defini uma variável de ambiente para informar o nome correto do cookie.&lt;/p&gt;

&lt;p&gt;Essa é uma implementação simples que resolve o problema.&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;cookies&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;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SignIn&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;cookieStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookies&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;csrfToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookieStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXTAUTH_CSRF_TOKEN&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/auth/callback/credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csrfToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;csrfToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Digite seu Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Digite sua Senha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Entrar&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;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;



</description>
      <category>nextauth</category>
      <category>nextjs</category>
      <category>approuter</category>
    </item>
    <item>
      <title>Resolvendo problema de "foreign key constraint" ao migrar o Ghost CMS da v4 para v5</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Fri, 05 May 2023 23:59:07 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/resolvendo-problema-de-foreign-key-constraint-ao-migrar-o-ghost-cms-da-v4-para-v5-1d41</link>
      <guid>https://dev.to/marcelo-albuquerque/resolvendo-problema-de-foreign-key-constraint-ao-migrar-o-ghost-cms-da-v4-para-v5-1d41</guid>
      <description>&lt;p&gt;Ao atualizarmos o Ghost para sua versão 5, diversos scripts de migração são executados, incluindo scripts que realizam alterações no Banco de Dados. E é justamente um desses scripts que ao não conseguir realizar uma mudança na base de dados, impossibilita a execução do Ghost.&lt;/p&gt;

&lt;p&gt;Esse script tenta realizar uma mudança que afeta uma tabela com &lt;code&gt;foreign key constraint&lt;/code&gt; , o que é bloqueado pelo Banco de Dados.&lt;/p&gt;

&lt;p&gt;O script em questão, tenta executar o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="nv"&gt;`subscriptions`&lt;/span&gt; &lt;span class="k"&gt;modify&lt;/span&gt;  &lt;span class="nv"&gt;`tier_id`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos observar que ele tenta executar uma mudança na tabela &lt;code&gt;subscriptions&lt;/code&gt; e nos retorna o seguinte erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Message: Ghost was able to start, but errored during boot with: alter table &lt;span class="sb"&gt;`&lt;/span&gt;subscriptions&lt;span class="sb"&gt;`&lt;/span&gt; modify  &lt;span class="sb"&gt;`&lt;/span&gt;tier_id&lt;span class="sb"&gt;`&lt;/span&gt; varchar&lt;span class="o"&gt;(&lt;/span&gt;24&lt;span class="o"&gt;)&lt;/span&gt; not null  - Cannot change column &lt;span class="s1"&gt;'tier_id'&lt;/span&gt;: used &lt;span class="k"&gt;in &lt;/span&gt;a foreign key constraint &lt;span class="s1"&gt;'subscriptions_tier_id_foreign'&lt;/span&gt;
Context: &lt;span class="o"&gt;[&lt;/span&gt;object Object]
Help: Error occurred &lt;span class="k"&gt;while &lt;/span&gt;executing the following migration: 2022-10-18-05-39-drop-nullable-tier-id.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para solucionar esse problema devemos desabilitar a checagem &lt;code&gt;foreign keys&lt;/code&gt; antes de executar o script, após a execução da &lt;code&gt;query&lt;/code&gt; podemos reabilitar a checagem normalmente. O script a seguir deve ser executado diretamente na base de dados de forma manual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;ghost_test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Aqui&lt;/span&gt; &lt;span class="n"&gt;seleciono&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;dados&lt;/span&gt; &lt;span class="n"&gt;onde&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tabela&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt; &lt;span class="n"&gt;encontra&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;foreign_key_checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Desabilito&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;checagens&lt;/span&gt; &lt;span class="k"&gt;foreign&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;
&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="nv"&gt;`subscriptions`&lt;/span&gt; &lt;span class="k"&gt;modify&lt;/span&gt;  &lt;span class="nv"&gt;`tier_id`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Executo&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;que&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Ghost&lt;/span&gt; &lt;span class="n"&gt;tentou&lt;/span&gt; &lt;span class="n"&gt;realizar&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;foreign_key_checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Reabilito&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;checagens&lt;/span&gt; &lt;span class="k"&gt;foreign&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, de volta ao Ghost, podemos executar o comando para iniciar sua execução:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ghost start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Processo finalizado!&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Instalação e configuração do Strapi com Amazon Linux 2023</title>
      <dc:creator>Marcelo Albuquerque</dc:creator>
      <pubDate>Wed, 03 May 2023 17:20:35 +0000</pubDate>
      <link>https://dev.to/marcelo-albuquerque/instalacao-e-configuracao-do-strapi-com-amazon-linux-2023-1dpa</link>
      <guid>https://dev.to/marcelo-albuquerque/instalacao-e-configuracao-do-strapi-com-amazon-linux-2023-1dpa</guid>
      <description>&lt;p&gt;Nunca havia utilizado o Strapi de forma efetiva anteriormente, apenas realizei pequenos testes para tentar entender sua lógica. A instalação no ambiente local não oferece nenhum "desafio" por assim dizer, basta seguir a &lt;a href="https://docs.strapi.io/dev-docs/installation/cli"&gt;documentação oficial&lt;/a&gt; e nenhum problema deve surgir.&lt;/p&gt;

&lt;p&gt;A coisa começa a ganhar contornos complexos de fato, no ambiente de produção, que é o foco desta documentação que vos escrevo.&lt;/p&gt;

&lt;p&gt;Como em produção trabalho constantemente com o ambiente da AWS, esta documentação é focada no &lt;a href="https://aws.amazon.com/pt/linux/amazon-linux-2023/"&gt;Amazon Linux 2023&lt;/a&gt;, que podemos simplesmente considerá-la como uma distribuição Linux desenvolvida pela AWS, bem como a Amazon Linux 2. São sistemas desenvolvidos para se integrarem de forma otimizada a serviços da AWS. Porém, nada impede que possamos utilizar esta documentação em outros ambientes como o Ubuntu, basta seguir a mesma lógica.&lt;/p&gt;

&lt;p&gt;E preciso dizer que uma das maiores motivações que tive para escrever esta documentação, foi justamente a falta de uma documentação oficial bem detalhada focada em ambientes de produção. Chega a ser curioso e acredito não ser algo exclusivo do &lt;strong&gt;Strapi&lt;/strong&gt;, mas me estranha uma ferramenta tão popular falhar nesse aspecto. Devemos lembrar que o Strapi possuí uma solução gerenciada, porém, os valores podem afastar e até assustar a maioria dos interessados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparação do ambiente
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Usuário
&lt;/h3&gt;

&lt;p&gt;Vamos começar falando sobre o usuário. Na Amazon Linux 2023, bem como, na Amazon Linux 2 o usuário padrão é o "ec2-user" e é justamente com esse usuário que realizaremos todas as configurações, não sendo necessário mudar para o "root" ou criar algum outro usuário.&lt;/p&gt;

&lt;h3&gt;
  
  
  Servidor Web
&lt;/h3&gt;

&lt;p&gt;Aqui estamos utilizando o NGINX que pode ser facilmente instalado com o gerenciador de pacotes &lt;code&gt;dnf&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.js
&lt;/h3&gt;

&lt;p&gt;Vamos utilizar o Node.js que também pode ser facilmente instalado com o gerenciador de pacotes &lt;code&gt;dnf&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Estrutura de pastas
&lt;/h3&gt;

&lt;p&gt;Tenho o costume de criar a estrutura de pastas para o NGINX em &lt;code&gt;/var/www&lt;/code&gt; , porém, pode ser que você estruture em outro local. Caso queira utilizar a estrutura deste exemplo, basta seguir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora podemos navegar até a pasta &lt;code&gt;www&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos criar a pasta para nosso projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir &lt;/span&gt;strapiapp.com.br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A aplicação será executada pelo usuário atual, porém, como estamos trabalhando com os diretórios de sistema, utilizamos o comando &lt;code&gt;sudo&lt;/code&gt; para criar as pastas necessárias. Portanto, devemos alterar o &lt;code&gt;owner&lt;/code&gt; do diretório do projeto para que todas as execuções não necessitem do comando &lt;code&gt;sudo&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;ec2-user:ec2-user strapiapp.com.br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Também vamos definir permissões para o diretório do projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;775 strapiapp.com.br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E finalmente navegamos até o diretório do projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;strapiapp.com.br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado é a seguinte estrutura de diretórios: &lt;code&gt;/var/www/strapiapp.com.br&lt;/code&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalação do Strapi
&lt;/h2&gt;

&lt;p&gt;Vamos instalar a última versão do Strapi a partir do diretório atual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-strapi-app@latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;## Observer que não estamos utilizando o comando sudo&lt;/span&gt;

&lt;span class="c"&gt;# 'npx' executa o comando de um pacote npm&lt;/span&gt;
&lt;span class="c"&gt;# 'create-strapi-app' é o pacote Strapi&lt;/span&gt;
&lt;span class="c"&gt;# '@latest' indica que vamos instalar a última versão do Strapi&lt;/span&gt;
&lt;span class="c"&gt;# '.' informo que desejo utilizar o diretório atual para a instalação sem a necessidade de criar um novo diretório e informar o nome do projeto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso ainda não tenha o pacote create-strapi-app instalado, será informado que é necessário realizar sua instalação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Need to &lt;span class="nb"&gt;install &lt;/span&gt;the following packages:
  create-strapi-app@4.10.2
Ok to proceed? &lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basta confirmar e seguir.&lt;/p&gt;

&lt;p&gt;Após o processo de instalação do pacote ser finalizada, necessitamos informar as configurações do nosso projeto.&lt;/p&gt;

&lt;p&gt;Para este projeto estou trabalhando com um Banco de Dados MySQL configurado e instalado em outro host. A primeira questão é referente ao modo de configuração, e aqui seleciono o modo "Custom (manual settings)", pois pretendo configurar esse projeto manualmente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Choose your installation &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Use arrow keys&lt;span class="o"&gt;)&lt;/span&gt;
  Quickstart &lt;span class="o"&gt;(&lt;/span&gt;recommended&lt;span class="o"&gt;)&lt;/span&gt;
❯ Custom &lt;span class="o"&gt;(&lt;/span&gt;manual settings&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# A diferença entre esses modos é que no "Quickstart (recommended)" uma base de dados (SQLite) é criada localmente para facilitar a criação do projeto. Porém, estamos falando de configurar um projeto em Produção e essa configuração básica não atende nossas necessidades.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida somos questionados sobre a nossa preferência de linguagem entre "Javascript" e "Typescript":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Choose your preferred language
  JavaScript
❯ TypeScript

&lt;span class="c"&gt;# Aqui estou selecionando Typescript por uma preferência estritamente pessoal, selecione a linguagem que lhe trouxer mais conforto.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora somos questionados sobre qual banco de dados vamos utilizar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Choose your default database client
  sqlite
  postgres
❯ mysql

&lt;span class="c"&gt;# Como informei anteriormente, já possuo um cliente MySQL previamente configurado em outro host, então simplesmente seleciono a opção "mysql"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Informe o nome do seu banco de dados utilizado para o projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Database name: strapi_app

&lt;span class="c"&gt;# Previamente criei uma base de dados com o nome "strapi_app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Informe o host de seu banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Host: host.domain.us-east-1.rds.amazonaws.com

&lt;span class="c"&gt;# Observe que o domínio "host.domain.us-east-1.rds.amazonaws.com" é apenas um exemplo, informe aqui o host do seu banco de dados.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Informe a porta de comunicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Port: &lt;span class="o"&gt;(&lt;/span&gt;3306&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# A porta de comunicação padrão é a 3306, informe outro valor caso seja o seu caso.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora informe o usuário de seu banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Username: admin

&lt;span class="c"&gt;# Aqui informo que o nome do usuário é admin, informe o valor correto para seu caso.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Senha de acesso ao banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Password: &lt;span class="k"&gt;******&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Informe se sua conexão de banco de dados possuí SSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Enable SSL connection: &lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt; N

&lt;span class="c"&gt;# No meu caso, não utilizo SSL para me comunicar com o bando de dados.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O processo de instalação será iniciado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Creating a project with custom database options.
Creating a new Strapi application at /var/www/strapiapp.com.br.
Creating files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao final do processo, caso tudo tenha transcorrido corretamente, receberemos a seguinte mensagem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Dependencies installed successfully.
Initialized a git repository.


Your application was created at /var/www/strapiapp.com.br.

Available commands &lt;span class="k"&gt;in &lt;/span&gt;your project:

  yarn develop
  Start Strapi &lt;span class="k"&gt;in &lt;/span&gt;watch mode. &lt;span class="o"&gt;(&lt;/span&gt;Changes &lt;span class="k"&gt;in &lt;/span&gt;Strapi project files will trigger a server restart&lt;span class="o"&gt;)&lt;/span&gt;

  npm run start
  Start Strapi without watch mode.

  npm run build
  Build Strapi admin panel.

  npm run strapi
  Display all available commands.

You can start by doing:

  &lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/strapiapp.com.br
  npm run develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Trabalhando com o Strapi
&lt;/h2&gt;

&lt;p&gt;Algo que também me incomodou em relação a documentação e tutoriais espalhados pela internet é a falta de uma análise mais técnica em relação ao Strapi, ou seja, como de fato ele funciona, como ele pode me ajudar em um ambiente local, e principalmente como ele deve ser instalado e configurado em um ambiente de produção.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como o Strapi funciona?
&lt;/h3&gt;

&lt;p&gt;De uma forma simples e direta, para que você consiga estruturar um projeto utilizando o Strapi, você sempre irá executá-lo no modo "develop" e após ter criado toda estrutura utilizando o "Content-Type Builder" é que vamos gerar o "Build" para enfim rodarmos em produção no modo "start".&lt;/p&gt;

&lt;p&gt;Você não conseguirá criar nenhum "Content-Type" no modo "start", caso tente, receberá a seguinte mensagem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The autoReload feature is required to use this plugin. Start your server with &lt;span class="sb"&gt;`&lt;/span&gt;strapi develop&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Primeiros passos
&lt;/h3&gt;

&lt;p&gt;Mesmo sem termos criado nenhum "Content-Type", podemos gerar o &lt;code&gt;build&lt;/code&gt; inicial para termos acesso a área administrativa:&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 build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E enfim, executamos o comando para executar de fato o Strapi:&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Strapi será executado na porta &lt;code&gt;1337&lt;/code&gt; .&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando Proxy Reverso no NGINX
&lt;/h3&gt;

&lt;p&gt;Aqui vou deixar um exemplo simples de como configurar um proxy reverso com redirecionamento automático para &lt;code&gt;https&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
    listen 80&lt;span class="p"&gt;;&lt;/span&gt;
    listen &lt;span class="o"&gt;[&lt;/span&gt;::]:80&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'strapiapp.com.br'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;301 https://strapiapp.com.br&lt;span class="nv"&gt;$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

server &lt;span class="o"&gt;{&lt;/span&gt;
    listen 443 ssl&lt;span class="p"&gt;;&lt;/span&gt;
    listen &lt;span class="o"&gt;[&lt;/span&gt;::]:443 ssl&lt;span class="p"&gt;;&lt;/span&gt;

    server_name strapiapp.com.br&lt;span class="p"&gt;;&lt;/span&gt;
    root /var/www/strapiapp.com.br&lt;span class="p"&gt;;&lt;/span&gt;

    ssl_certificate &lt;span class="s2"&gt;"/etc/letsencrypt/live/strapiapp.com.br/fullchain.pem"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    ssl_certificate_key &lt;span class="s2"&gt;"/etc/letsencrypt/live/strapiapp.com.br/privkey.pem"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    location / &lt;span class="o"&gt;{&lt;/span&gt;
        proxy_set_header X-Forwarded-For &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-Proto &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        proxy_set_header X-Real-IP &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        proxy_set_header Host &lt;span class="nv"&gt;$http_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        proxy_pass http://127.0.0.1:1337&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Criando um serviço utilizando Systemd
&lt;/h3&gt;

&lt;p&gt;Para que a execução do Strapi seja automática, ela deve ser controlada pelo sistema e não pela interação com o terminal de comandos, portanto, vou detalhar os passos para que seja criado um serviço "Systemd" que irá persistir mesmo após a reinicialização do sistema.&lt;/p&gt;

&lt;p&gt;Diretório que armazena os serviços "Systemd":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/lib/systemd/system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos navegar até esse diretório:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /lib/systemd/system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos criar um arquivo para armazenar as informações de execução desse serviço:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano strapi_strapiapp-com-br.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O arquivo deve conter as seguintes informações:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Strapi systemd service &lt;span class="k"&gt;for &lt;/span&gt;project: strapiapp-com-br
&lt;span class="nv"&gt;Documentation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://docs.strapi.io/dev-docs/deployment

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;simple
&lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/strapiapp.com.br
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ec2-user
&lt;span class="nv"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV=production"&lt;/span&gt;
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/npm run start
&lt;span class="nv"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;CI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora podemos ativar esse serviço para que sua execução seja persistente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;strapi_strapiapp-com-br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E finalmente podemos excutar o Strapi através do "Systemd":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start strapi_strapiapp-com-br
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalizado!&lt;/p&gt;

&lt;p&gt;Esta é uma documentação técnica que abrange os principais pontos para executar o Strapi em um ambiente de produção. Espero que tenha sido útil pra você, qualquer coisa me chama para um bate-papo!&lt;/p&gt;

</description>
      <category>strapi</category>
      <category>aws</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
