<?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>Forem: Junior</title>
    <description>The latest articles on Forem by Junior (@antoniojuniordev).</description>
    <link>https://forem.com/antoniojuniordev</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%2F590217%2F1a970f89-bd32-4eae-89da-b5ec3e8c2761.jpeg</url>
      <title>Forem: Junior</title>
      <link>https://forem.com/antoniojuniordev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/antoniojuniordev"/>
    <language>en</language>
    <item>
      <title>Como implementar internacionalização (i18n) em um projeto Next.js sem alterar a URL</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Tue, 10 Jun 2025 10:26:32 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/como-implementar-internacionalizacao-i18n-em-um-projeto-nextjs-sem-alterar-a-url-8kb</link>
      <guid>https://forem.com/antoniojuniordev/como-implementar-internacionalizacao-i18n-em-um-projeto-nextjs-sem-alterar-a-url-8kb</guid>
      <description>&lt;p&gt;Neste artigo, vou mostrar de forma objetiva e direta como implementar a internacionalização (i18n) em um projeto Next.js utilizando o &lt;code&gt;next-intl&lt;/code&gt;, sem vincular os idiomas às rotas da URL — ou seja, nada de caminhos como &lt;code&gt;example.com/en&lt;/code&gt;. Essa abordagem evita a necessidade de tratamentos extras caso o usuário altere a URL manualmente.&lt;/p&gt;

&lt;p&gt;Utilizaremos um cookie para identificar e armazenar o idioma selecionado pelo usuário. Esse cookie será definido automaticamente no primeiro acesso ao site. Caso o usuário deseje alterar o idioma depois, será possível fazer essa mudança pela plataforma desenvolvida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuração inicial
&lt;/h2&gt;

&lt;p&gt;Caso ainda não tenha um projeto Next.js criado, você pode seguir este tutorial: &lt;a href="https://nextjs.org/docs/app/getting-started/installation" rel="noopener noreferrer"&gt;Como configurar um novo projeto Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Agora, instale o pacote &lt;code&gt;next-intl&lt;/code&gt;, que nos ajudará a configurar o i18n.&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  next-intl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos estruturar as pastas do projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── src
│   ├── app
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components
│   ├── hooks
│   └── i18n
│       ├── locales
│       │   ├── en-US.ts
│       │   └── pt-BR.ts
│       ├── config.ts
│       ├── locale.ts
│       └── request.ts
│   ├── services
│   └── styles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hoje vamos focar apenas na pasta &lt;code&gt;i18n&lt;/code&gt;. Em um próximo artigo, falarei mais sobre organização de pastas.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Configurando o arquivo &lt;code&gt;layout.tsx&lt;/code&gt; (ponto de entrada)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/app/layout.tsx&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;import { Metadata } from 'next'
import { NextIntlClientProvider } from 'next-intl'
import { getLocale, getMessages } from 'next-intl/server'
import { ReactNode } from 'react'

export const metadata: Metadata = {
  title: 'NextJS',
  description: 'Site do NextJS'
}

async function RootLayout({ children }: { children: ReactNode }) {
  const locale = await getLocale()
  const messages = await getMessages()

  return (
    &amp;lt;html lang={locale}&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;NextIntlClientProvider messages={messages}&amp;gt;
          {children}
        &amp;lt;/NextIntlClientProvider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, configuramos o &lt;code&gt;NextIntlClientProvider&lt;/code&gt;, que recebe as mensagens do &lt;code&gt;getMessages()&lt;/code&gt; e o idioma atual com &lt;code&gt;getLocale()&lt;/code&gt; que ira para a tag html, ambos importados do &lt;code&gt;next-intl/server&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 - Configurando a pasta &lt;code&gt;i18n&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Essa estrutura serve para guardar todas as configurações relacionadas ao uso do i18n no projeto.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 - Arquivo de &lt;code&gt;config.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/config.ts&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;export type LocaleProps = (typeof locales)[number]

export const locales = ['en-US', 'pt-BR'] as const
export const defaultLocale: LocaleProps = 'en-US'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui exportamos as tipagens das idiomas que vamos usar no projeto.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 - Arquivo de &lt;code&gt;locale.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/locale.ts&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;'use server'

import { cookies } from 'next/headers'

import { defaultLocale, LocaleProps } from './config'

const COOKIE_NAME = `${process.env.NEXT_PUBLIC_PROJECT_NAME}-i18n`

export async function getUserLocale() {
  return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale
}

export async function setUserLocale(locale: LocaleProps) {
  ;(await cookies()).set(COOKIE_NAME, locale)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse arquivo é feito pra rodar no server, aqui gerenciamos todo buscar e alteração do cookie, ficar por sua escolha colocar o nome do projeto ou não para compor como vai ser chamado no cookie.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 - Arquivo de &lt;code&gt;request.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/request.ts&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;import {
  useLocale as useNextIntlLocale,
  useTranslations as useNextIntlTranslations
} from 'next-intl'
import { getRequestConfig, setRequestLocale } from 'next-intl/server'

import { getUserLocale } from './locale'

export default getRequestConfig(async () =&amp;gt; {
  const locale = await getUserLocale()
  return {
    locale,
    messages: (await import(`./locales/${locale}.ts`)).default
  }
})

export function setLocale(locale: 'pt-BR' | 'en-US') {
  setRequestLocale(locale)
}

export function useLocale() {
  const locale = useNextIntlLocale()
  return locale
}

export function useTranslations() {
  const t = useNextIntlTranslations()
  return t as (key: string) =&amp;gt; string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O arquivo &lt;code&gt;request&lt;/code&gt; deixou para buscar e montagem do json dos locales(Que vem da pasta locales que vou explicar logo abaixo) um &lt;code&gt;wrapper&lt;/code&gt; de atualizar a linguagem, buscar a que está ativa no momento e também um para tradução. Esse formato ajuda caso um dia chegue a trocar a lib &lt;code&gt;next-intl&lt;/code&gt;, só preciso trocar nessas funções e todo o projeto continuará funcionando.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 - Pasta &lt;code&gt;locales&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Essa pasta contem os arquivos que guardam todos as traduções do nosso projeto. &lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  English: 'English',
  Portuguese: 'Portuguese',
 'Page not found': 'Page not found',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;src/i18n/locales/en-US.ts&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;export default {
  English: 'Inglês',
  Portuguese: 'Português',
 'Page not found': 'Página não encontrada',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;src/i18n/locales/pt-BR.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Um detalhe: utilizo as chaves das traduções como o próprio texto (no padrão inglês). Isso facilita o fallback quando há erro na biblioteca de tradução — ao menos o sistema exibe o texto em inglês. Além disso, evita duplicação e facilita a leitura e manutenção do JSON, especialmente em sistemas grandes.&lt;/p&gt;

&lt;p&gt;Exemplo de uso normal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  notFound: 'Página não encontrada',
  pageNotFound: 'Página não encontrada',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso é fácil de gerenciar em arquivos pequenos, mas vira um grande desafio em projetos grandes.&lt;/p&gt;

&lt;p&gt;Exemplo de uso 1&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('title')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exemplo de uso 2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eu particularmente prefiro o Exemplo de uso 1.&lt;/p&gt;

&lt;p&gt;Atenção: O next-intl pode gerar erro se a chave tiver um ponto no final, tipo 'Home page.'. Isso não acontece com a lib react-i18next (tema para um próximo artigo 😄).&lt;/p&gt;

&lt;p&gt;Um outro ponto que gosto de fazer com essa arquitetura de organização é separar os json de tradução do projeto.&lt;/p&gt;

&lt;p&gt;Por exemplo.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcgnubkm2otki9z8cq8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcgnubkm2otki9z8cq8j.png" alt="Imagem de como ficar as pastas" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesse exemplo temos duas pasta de i18n uma em cada modulo(Padrão de modulo consistem em centralizar tudo em seus modulos como o de Auth, Home, User, Profile, Company para facilitar uma manutenção e merge posso trabalhar esse ponto em artigos futuro 😄).&lt;/p&gt;

&lt;p&gt;Agora vamos ver como ficaria o nosso arquivo de locales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Auth from '@/app/(auth)/i18n/en-US'
import Dashboard from '@/app/(dashoard)/i18n/en-US'

export default {
  English: 'English',
  'Page not found': 'Page not found',
  ...Auth,
  ...Dashboard 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o uso do spread, mantemos os arquivos menores e organizados. E como usamos o próprio texto como chave, evitamos conflitos entre módulos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vamos usar o que construímos
&lt;/h2&gt;

&lt;p&gt;Para usar em nosso projeto tem algumas abordagem a mais simples seria assim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porém esse formato você já viu, vamos melhorar ele?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

interface TypographyProps {
  text: string
  className?: string
  length?: number
  variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'strong'
}

function Typography({ variant, text, className, length }: TypographyProps) {
  const Component = variant || 'span'
  const t = useTranslations()
  const textTranslation = t(text)

  return (
    &amp;lt;Component className={className}&amp;gt;
      {length &amp;amp;&amp;amp; textTranslation.length &amp;gt; length ? (
        &amp;lt;&amp;gt;{textTranslation.substring(0, length)}...&amp;lt;/&amp;gt;
      ) : (
        textTranslation
      )}
    &amp;lt;/Component&amp;gt;
  )
}

export { Typography }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui criamos um componente &lt;code&gt;Typography&lt;/code&gt; que ele pode ser qualquer uma das tags html que ele recebe como prop, e o mesmo também recebe uma prop &lt;code&gt;text&lt;/code&gt; que já vai ser traduzida no próprio componente, além disso, pode passar uma &lt;code&gt;lenght&lt;/code&gt; para um limite no texto a ser exibido. Assim não precisa em todo arquivo do seu projeto ficar importando o &lt;code&gt;useTranslations()&lt;/code&gt; esse mesmo formato pode se estender para label de input e etc.&lt;/p&gt;

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

&lt;p&gt;Existem várias formas de organizar e aplicar i18n no seu projeto. Neste artigo, compartilhei um modelo que gosto e acho bastante produtivo. Ele pode não ser o "melhor", mas é funcional, escalável e fácil de manter. Sinta-se à vontade para adaptá-lo e evoluí-lo conforme as necessidades dos seus projetos.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>português</category>
    </item>
    <item>
      <title>How to implement internationalization (i18n) in a Next.js project without changing the URL</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Tue, 10 Jun 2025 10:26:28 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/how-to-implement-internationalization-i18n-in-a-nextjs-project-without-changing-the-url-1g70</link>
      <guid>https://forem.com/antoniojuniordev/how-to-implement-internationalization-i18n-in-a-nextjs-project-without-changing-the-url-1g70</guid>
      <description>&lt;p&gt;In this article, I will objectively and directly show how to implement internationalization (i18n) in a Next.js project using &lt;code&gt;next-intl&lt;/code&gt;, without tying languages to the URL paths — in other words, no paths like &lt;code&gt;example.com/en&lt;/code&gt;. This approach avoids the need for extra handling in case the user manually changes the URL.&lt;/p&gt;

&lt;p&gt;We'll use a cookie to identify and store the user's selected language. This cookie will be automatically set on the user's first visit to the site. If the user wishes to change the language later, they’ll be able to do so through the developed platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial setup
&lt;/h2&gt;

&lt;p&gt;If you don’t have a Next.js project set up yet, you can follow this tutorial: &lt;a href="https://nextjs.org/docs/app/getting-started/installation" rel="noopener noreferrer"&gt;How to set up a new Next.js project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, install the &lt;code&gt;next-intl&lt;/code&gt; package, which will help us configure i18n.&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  next-intl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s structure the project folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── src
│   ├── app
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components
│   ├── hooks
│   └── i18n
│       ├── locales
│       │   ├── en-US.ts
│       │   └── pt-BR.ts
│       ├── config.ts
│       ├── locale.ts
│       └── request.ts
│   ├── services
│   └── styles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today we’ll focus only on the &lt;code&gt;i18n&lt;/code&gt; folder. In a future article, I’ll talk more about folder organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Configuring the layout.tsx file (entry point)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/app/layout.tsx&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;import { Metadata } from 'next'
import { NextIntlClientProvider } from 'next-intl'
import { getLocale, getMessages } from 'next-intl/server'
import { ReactNode } from 'react'

export const metadata: Metadata = {
  title: 'NextJS',
  description: 'Site do NextJS'
}

async function RootLayout({ children }: { children: ReactNode }) {
  const locale = await getLocale()
  const messages = await getMessages()

  return (
    &amp;lt;html lang={locale}&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;NextIntlClientProvider messages={messages}&amp;gt;
          {children}
        &amp;lt;/NextIntlClientProvider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we configure the &lt;code&gt;NextIntlClientProvider&lt;/code&gt;, which receives messages from &lt;code&gt;getMessages()&lt;/code&gt; and the current language from &lt;code&gt;getLocale()&lt;/code&gt;, both imported from &lt;code&gt;next-intl/server&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 - Setting up the &lt;code&gt;i18n&lt;/code&gt; folder
&lt;/h3&gt;

&lt;p&gt;This structure stores all configurations related to i18n usage in the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 - &lt;code&gt;config.ts&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/config.ts&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;export type LocaleProps = (typeof locales)[number]

export const locales = ['en-US', 'pt-BR'] as const
export const defaultLocale: LocaleProps = 'en-US'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we export the language typings that will be used in the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 - &lt;code&gt;locale.ts&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/locale.ts&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;'use server'

import { cookies } from 'next/headers'

import { defaultLocale, LocaleProps } from './config'

const COOKIE_NAME = `${process.env.NEXT_PUBLIC_PROJECT_NAME}-i18n`

export async function getUserLocale() {
  return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale
}

export async function setUserLocale(locale: LocaleProps) {
  ;(await cookies()).set(COOKIE_NAME, locale)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file runs on the server. Here, we manage reading and updating the cookie. It’s up to you whether or not to include the project name as part of the cookie key.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 - &lt;code&gt;request.ts&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;src/i18n/request.ts&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;import {
  useLocale as useNextIntlLocale,
  useTranslations as useNextIntlTranslations
} from 'next-intl'
import { getRequestConfig, setRequestLocale } from 'next-intl/server'

import { getUserLocale } from './locale'

export default getRequestConfig(async () =&amp;gt; {
  const locale = await getUserLocale()
  return {
    locale,
    messages: (await import(`./locales/${locale}.ts`)).default
  }
})

export function setLocale(locale: 'pt-BR' | 'en-US') {
  setRequestLocale(locale)
}

export function useLocale() {
  const locale = useNextIntlLocale()
  return locale
}

export function useTranslations() {
  const t = useNextIntlTranslations()
  return t as (key: string) =&amp;gt; string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;request&lt;/code&gt; file handles retrieving and assembling the locale JSONs (from the &lt;code&gt;locales&lt;/code&gt; folder, which I’ll explain below), updating the language, retrieving the active one, and also a translation wrapper. This format helps in case we ever want to change the &lt;code&gt;next-intl&lt;/code&gt; library — we’d only need to update these functions and the whole project would still work.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 - &lt;code&gt;locales&lt;/code&gt; folder
&lt;/h4&gt;

&lt;p&gt;This folder contains the files that store all translations of our project.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  English: 'English',
  Portuguese: 'Portuguese',
 'Page not found': 'Page not found',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;src/i18n/locales/en-US.ts&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;export default {
  English: 'Inglês',
  Portuguese: 'Português',
 'Page not found': 'Página não encontrada',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;src/i18n/locales/pt-BR.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;One detail: I use the actual text (in English) as the translation keys. This simplifies fallbacks when there’s an error in the translation library — at least the system shows the English text. Also, it avoids duplication and makes the JSON easier to read and maintain, especially in large systems.&lt;/p&gt;

&lt;p&gt;Standard usage example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('title')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I personally prefer Example 1.&lt;/p&gt;

&lt;p&gt;Attention: &lt;code&gt;next-intl&lt;/code&gt; may throw an error if a key ends with a period, like &lt;code&gt;'Home page.'&lt;/code&gt;. This doesn’t happen with the &lt;code&gt;react-i18next&lt;/code&gt; library (topic for a future article 😄).&lt;/p&gt;

&lt;p&gt;Another thing I like about this folder organization is separating the project’s translation JSONs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcgnubkm2otki9z8cq8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcgnubkm2otki9z8cq8j.png" alt="Image of how the folders will look" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, we have two i18n folders — one in each module (a module pattern centralizes everything within its module, such as Auth, Home, User, Profile, Company — making maintenance and merges easier. I’ll go into detail in a future article 😄).&lt;/p&gt;

&lt;p&gt;Now let’s see how our &lt;code&gt;locales&lt;/code&gt; file would look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Auth from '@/app/(auth)/i18n/en-US'
import Dashboard from '@/app/(dashoard)/i18n/en-US'

export default {
  English: 'English',
  'Page not found': 'Page not found',
  ...Auth,
  ...Dashboard 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using spread syntax, we keep files small and organized. And by using the text itself as keys, we avoid conflicts between modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s use what we’ve built
&lt;/h2&gt;

&lt;p&gt;To use this in our project, the simplest approach would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page')}&amp;lt;/h1&amp;gt;
      &amp;lt;h1&amp;gt;{t('Home page subtitle')}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But since you've already seen this format, let’s improve it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTranslations } from 'next-intl'

interface TypographyProps {
  text: string
  className?: string
  length?: number
  variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'strong'
}

function Typography({ variant, text, className, length }: TypographyProps) {
  const Component = variant || 'span'
  const t = useTranslations()
  const textTranslation = t(text)

  return (
    &amp;lt;Component className={className}&amp;gt;
      {length &amp;amp;&amp;amp; textTranslation.length &amp;gt; length ? (
        &amp;lt;&amp;gt;{textTranslation.substring(0, length)}...&amp;lt;/&amp;gt;
      ) : (
        textTranslation
      )}
    &amp;lt;/Component&amp;gt;
  )
}

export { Typography }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we created a &lt;code&gt;Typography&lt;/code&gt; component that can be any HTML tag, received via the &lt;code&gt;variant&lt;/code&gt; prop. It also accepts a &lt;code&gt;text&lt;/code&gt; prop that will be translated inside the component. You can also set a &lt;code&gt;length&lt;/code&gt; limit on the displayed text. This way, you don’t need to import &lt;code&gt;useTranslations()&lt;/code&gt; in every file — you can extend this same pattern for inputs, labels, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are many ways to organize and apply i18n in your project. In this article, I shared a model that I personally like and find very productive. It might not be the "best", but it’s functional, scalable, and easy to maintain. Feel free to adapt and evolve it based on your project's needs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>english</category>
    </item>
    <item>
      <title>Implementando tema dark e light no Next.js com Tailwind</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Mon, 28 Apr 2025 14:15:12 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/implementando-tema-dark-e-light-no-nextjs-com-tailwind-295p</link>
      <guid>https://forem.com/antoniojuniordev/implementando-tema-dark-e-light-no-nextjs-com-tailwind-295p</guid>
      <description>&lt;h2&gt;
  
  
  Primeiramente, por que precisamos implementar tema dark e light nos nossos projetos?
&lt;/h2&gt;

&lt;p&gt;Ter esse recurso não é um requisito que fará seu projeto ter ou não mais acesso, porém ajuda muito na experiência de uso, pois existem pessoas com diferentes preferências. Entendido esse ponto, vamos ao que interessa! 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo o recurso no Tailwind
&lt;/h2&gt;

&lt;p&gt;Para configurarmos esse recurso no Tailwind, precisamos apenas adicionar ou remover a classe &lt;code&gt;dark&lt;/code&gt; no elemento &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; do HTML e usar o prefixo &lt;code&gt;dark:&lt;/code&gt; nas classes dos elementos que desejamos ter estilos diferentes para cada tema.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="bg-gray-800 dark:bg-gray-500"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse cenário, o fundo será &lt;code&gt;bg-gray-800&lt;/code&gt; como padrão (modo claro) e &lt;code&gt;bg-gray-500&lt;/code&gt; quando o tema escuro estiver ativo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona no Next.js
&lt;/h2&gt;

&lt;p&gt;No Next.js é um pouco diferente, pois é necessário o apoio de uma biblioteca chamada &lt;code&gt;next-themes&lt;/code&gt;.&lt;br&gt;
Importante: você não é obrigado a usá-la — é totalmente possível implementar o mesmo recurso manualmente — porém, ela facilita bastante o desenvolvimento.&lt;/p&gt;

&lt;p&gt;Vamos ver exemplos usando e não usando a biblioteca.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exemplo sem usar a biblioteca
&lt;/h2&gt;

&lt;p&gt;Aqui, você será responsável por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gerenciar o valor do tema no &lt;code&gt;localStorage&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Detectar qual tema o sistema do usuário está utilizando para proporcionar uma melhor experiência.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// hooks/useTheme.ts
import { useEffect, useState } from 'react';

type Theme = 'light' | 'dark';

export function useTheme() {
  const [theme, setTheme] = useState&amp;lt;Theme&amp;gt;('light');

  // Detecta preferências do sistema
  useEffect(() =&amp;gt; {
    const savedTheme = localStorage.getItem('theme') as Theme | null;
    if (savedTheme) {
      setTheme(savedTheme);
    } else {
      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      setTheme(prefersDark ? 'dark' : 'light');
    }
  }, []);

  // Atualiza a classe no &amp;lt;html&amp;gt; e salva no localStorage
  useEffect(() =&amp;gt; {
    const root = document.documentElement;
    if (theme === 'dark') {
      root.classList.add('dark');
      root.classList.remove('light');
    } else {
      root.classList.add('light');
      root.classList.remove('dark');
    }
    localStorage.setItem('theme', theme);
  }, [theme]);

  return { theme, setTheme };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Exemplo usando a biblioteca &lt;code&gt;next-themes&lt;/code&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// provider/theme-provider.ts
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'
import * as React from 'react'

function ThemeProvider({ children, ...props }: React.ComponentProps&amp;lt;typeof NextThemesProvider&amp;gt;) {
  const [mounted, setMounted] = React.useState(false)

  React.useEffect(() =&amp;gt; {
    setMounted(true)
  }, [])

  if (!mounted) {
    return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;
  }

  return &amp;lt;NextThemesProvider {...props}&amp;gt;{children}&amp;lt;/NextThemesProvider&amp;gt;
}

export { ThemeProvider }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Usando no layout raiz da aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function RootLayout({ children }: { children: ReactNode }) {
  return (
    &amp;lt;html lang="pt-BR"&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        &amp;gt;
          {children}
        &amp;lt;/ThemeProvider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao usar a lib &lt;code&gt;next-themes&lt;/code&gt;, ela já traz muitos recursos prontos, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;defaultTheme="system"&lt;/code&gt;, que automaticamente respeita a preferência do sistema do usuário;&lt;/li&gt;
&lt;li&gt;Controle do tema via classes (&lt;code&gt;class&lt;/code&gt;) no HTML;&lt;/li&gt;
&lt;li&gt;Entre outras configurações que você pode conferir na documentação oficial:
👉 &lt;a href="https://www.npmjs.com/package/next-themes#api" rel="noopener noreferrer"&gt;next-themes&lt;/a&gt; no npm&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Ter tema &lt;strong&gt;dark&lt;/strong&gt; e &lt;strong&gt;light&lt;/strong&gt; no seu sistema não é um ponto crucial, mas é &lt;strong&gt;muito importante&lt;/strong&gt; para melhorar a usabilidade e a interação do usuário com o seu projeto.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>português</category>
    </item>
    <item>
      <title>Implementing dark and light themes in Next.js with Tailwind</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Mon, 28 Apr 2025 14:14:59 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/implementing-dark-and-light-themes-in-nextjs-with-tailwind-1ki1</link>
      <guid>https://forem.com/antoniojuniordev/implementing-dark-and-light-themes-in-nextjs-with-tailwind-1ki1</guid>
      <description>&lt;h2&gt;
  
  
  First, why do we need to implement dark and light themes in our projects?
&lt;/h2&gt;

&lt;p&gt;Having this feature is not a requirement that will necessarily bring more traffic to your project, but it &lt;strong&gt;greatly improves the user experience&lt;/strong&gt;, as people have different preferences.&lt;br&gt;
With that understood, let's dive into how to do it! 😄&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the feature in Tailwind
&lt;/h2&gt;

&lt;p&gt;To configure this feature with Tailwind, you just need to &lt;strong&gt;add or remove&lt;/strong&gt; the &lt;code&gt;dark&lt;/code&gt; class on the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element and use the &lt;code&gt;dark:&lt;/code&gt; prefix in the elements’ classes that you want to style differently based on the theme.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="bg-gray-800 dark:bg-gray-500"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the default background color will be &lt;code&gt;bg-gray-800&lt;/code&gt; (light mode), and when the dark theme is active, it will switch to &lt;code&gt;bg-gray-500&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works in Next.js
&lt;/h2&gt;

&lt;p&gt;In Next.js, it’s a bit different, because you’ll likely need some help from a library called &lt;code&gt;next-themes&lt;/code&gt;.&lt;br&gt;
Important: it’s not mandatory — you can achieve the same functionality manually — but using the library makes the development process much easier.&lt;/p&gt;

&lt;p&gt;Let’s see examples both without and with the library.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example without using the library
&lt;/h2&gt;

&lt;p&gt;In this case, you are responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managing the theme value in &lt;code&gt;localStorage&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Detecting the user’s system theme to provide a better experience.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// hooks/useTheme.ts
import { useEffect, useState } from 'react';

type Theme = 'light' | 'dark';

export function useTheme() {
  const [theme, setTheme] = useState&amp;lt;Theme&amp;gt;('light');

  // Detect system preferences
  useEffect(() =&amp;gt; {
    const savedTheme = localStorage.getItem('theme') as Theme | null;
    if (savedTheme) {
      setTheme(savedTheme);
    } else {
      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      setTheme(prefersDark ? 'dark' : 'light');
    }
  }, []);

  // Update &amp;lt;html&amp;gt; class and save to localStorage
  useEffect(() =&amp;gt; {
    const root = document.documentElement;
    if (theme === 'dark') {
      root.classList.add('dark');
      root.classList.remove('light');
    } else {
      root.classList.add('light');
      root.classList.remove('dark');
    }
    localStorage.setItem('theme', theme);
  }, [theme]);

  return { theme, setTheme };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Example using the &lt;code&gt;next-themes&lt;/code&gt; library
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// provider/theme-provider.ts
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'
import * as React from 'react'

function ThemeProvider({ children, ...props }: React.ComponentProps&amp;lt;typeof NextThemesProvider&amp;gt;) {
  const [mounted, setMounted] = React.useState(false)

  React.useEffect(() =&amp;gt; {
    setMounted(true)
  }, [])

  if (!mounted) {
    return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;
  }

  return &amp;lt;NextThemesProvider {...props}&amp;gt;{children}&amp;lt;/NextThemesProvider&amp;gt;
}

export { ThemeProvider }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And using it in your root layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function RootLayout({ children }: { children: ReactNode }) {
  return (
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        &amp;gt;
          {children}
        &amp;lt;/ThemeProvider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using the &lt;code&gt;next-themes&lt;/code&gt; library, you get many built-in features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;defaultTheme="system"&lt;/code&gt;, which automatically picks up the user’s system preference;&lt;/li&gt;
&lt;li&gt;Managing theme changes through the &lt;code&gt;class&lt;/code&gt;attribute;&lt;/li&gt;
&lt;li&gt;Other customizations available in the official documentation:
👉 &lt;a href="https://www.npmjs.com/package/next-themes#api" rel="noopener noreferrer"&gt;next-themes&lt;/a&gt; on npm&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Having &lt;strong&gt;dark&lt;/strong&gt; and &lt;strong&gt;light&lt;/strong&gt; themes in your system is not absolutely critical, but it is &lt;strong&gt;very important&lt;/strong&gt; to improve usability and the overall user interaction with your project.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>english</category>
    </item>
    <item>
      <title>5 tips to improve backend integration, React with Axios</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Sun, 29 Jan 2023 15:02:05 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/5-tips-to-improve-backend-integration-react-with-axios-b3p</link>
      <guid>https://forem.com/antoniojuniordev/5-tips-to-improve-backend-integration-react-with-axios-b3p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In most frontend applications we have to integrate with the backend and with that comes several libraries that we can use such as fetch, ajax, axios among others and each one has its characteristics, advantages and disadvantages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99dh3eb9oews0mbisou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99dh3eb9oews0mbisou.png" alt="Bibliotecas de integrações com backend" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But regardless of which one we are going to use in our application, we have to think of points to help us with maintenance and better communication so as not to affect the usability of the user.&lt;/p&gt;

&lt;p&gt;In this post I will use axios with react and applying concepts that I consider extremely important that we should address in our applications. I will take into account that you already have a react project with axios installed.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  1 - Encapsulate service
&lt;/h2&gt;

&lt;p&gt;We must create a generic service called with the library that we chose to use for the integration and simply just use it in the application, with the same idea of components like card, inputs, among others that we already do.&lt;/p&gt;

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

&lt;p&gt;First we have to create a http.js or http.ts file (Remembering that you can put another name if you want) to export the axios with the base url of our backend already configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we must create another index.js or index.ts file where we will export the most commonly used http methods already wrapped in a try catch blog to deal with the errors of the calls right here. Here we are already using the file we created above http.ts to trigger the axios with the parameters, in future posts we will evolve this file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import http from './http';

export default {
  async get(url: string) {
    try {
      const response = await http.get(url);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async post(url: string, send: object) {
    try {
      const response = await http.post(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async put(url: string, send: object) {
    try {
      const response = await http.put(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async delete(url: string) {
    try {
      await http.delete(url);
      return true;
    } catch (err: any) {
      return false;
    }
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end we will have a folder structure like this.&lt;/p&gt;

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

&lt;p&gt;We'll be able to invoke the method on our components this way.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await services.post( '/authenticate', { email, password } );&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But why is it necessary to use this approach?&lt;/p&gt;

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

&lt;p&gt;When we work with a generic service and only import it into our application, it becomes simpler to maintain and modify later. See how we can do it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Add headers to all requests
&lt;/h2&gt;

&lt;p&gt;Now let's configure headers for all our requests, we'll need this point to pass token among other information that your backend may need as a business rule.&lt;/p&gt;

&lt;p&gt;Let's create an axios interceptors for this, as it's the best way to not just repeat code. See the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have already retrieved the localstorage token and added it to all calls to the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 - Redirect unauthorized or unauthenticated user
&lt;/h2&gt;

&lt;p&gt;We must have user redirection strategies when the user does not have authorization or permission so that he does not have the need to do this in our components.&lt;/p&gt;

&lt;p&gt;For that we must create another interceptors to deal with this process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

http.interceptors.response.use(
  (value) =&amp;gt; {
    return Promise.resolve(value);
  },
  (error) =&amp;gt; {
    const { isAxiosError = false, response = null } = error;

    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 401) {
      // User redirection rule for login page
      return Promise.reject(error);
    }
    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 403) {
      // User redirection rule for disallowed page
      return Promise.reject(error);
    }
    return Promise.reject(error);
  }
);

export default http;

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

&lt;/div&gt;



&lt;p&gt;Leave open where you want to send the user for both 401(Unauthenticated) and 403(Unauthorized). That way, even if the user manages to access a page that he couldn't, when the backend request comes back with the status code, the system will already direct him, this approach also works for when the token expires, which we'll see how to deal with later.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Request retry pattern
&lt;/h2&gt;

&lt;p&gt;Now we will need to apply a pattern retry to our requests so that our end user does not suffer from instabilities in the application as it may be undergoing a deploy or auto scaling of the infrastructure at the time of the call. For this we define a number of attempts in case the system returns error 500 or higher. Example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

http.interceptors.response.use(
  (value) =&amp;gt; {
    return Promise.resolve(value);
  },
  (error) =&amp;gt; {
    const { isAxiosError = false, response = null } = error;

    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 401) {
      // Regra de redirecionamento de usuário para página de login
      return Promise.reject(error);
    }
    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 403) {
      // Regra de redirecionamento de usuário para página de não permitido
      return Promise.reject(error);
    }
    return Promise.reject(error);
  }
);

let counter = 1;

http.interceptors.response.use(
  (response) =&amp;gt; {
    return response;
  },
  (error) =&amp;gt; {
    if (
      error.response.status &amp;gt;= 500 &amp;amp;&amp;amp;
      counter &amp;lt; Number(process.env.REACT_APP_RETRY)
    ) {
      counter++;
      return http.request(error.config);
    }
    counter = 1;
    return Promise.reject(error);
  }
);

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An interceptor was created so that it has a retry according to the number defined in &lt;strong&gt;process.env.REACT_APP_RETRY&lt;/strong&gt; times when the request has a status code greater than 500.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 - Refresh token
&lt;/h2&gt;

&lt;p&gt;When we work with authentication, it is a good practice and security rule to have tokens that expire so that the user does not stay logged in forever even without using the application.&lt;/p&gt;

&lt;p&gt;However, we have to solve the problem that if the token expires when the user cannot simply ask him to log in again, for that we have routes to refresh token.&lt;/p&gt;

&lt;p&gt;We can improve our index.ts file so that it does this automatically during your application's route calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import http from './http';

async function refreshToken() {
  const value = Number(localStorage.getItem('expired'));
  if (value &amp;amp;&amp;amp; new Date(value) &amp;lt; new Date()) {
    const result = await http.get('/refresh');
    localStorage.setItem('token', result.data.token);
    localStorage.setItem(
      'expired',
      String(new Date().setSeconds(result.data.expired))
    );
  }
}

export default {
  async get(url: string) {
    try {
      await refreshToken();
      const response = await http.get(url);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async post(url: string, send: object) {
    try {
      await refreshToken();
      const response = await http.post(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async put(url: string, send: object) {
    try {
      await refreshToken();
      const response = await http.put(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async delete(url: string) {
    try {
      await refreshToken();
      await http.delete(url);
      return true;
    } catch (err: any) {
      return false;
    }
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created a refreshToken() function that will always be called before all our application calls. It would check if the token's expired has already passed, and if so, make a new call to the backend, renewing the token and the expired. Remembering that this logic works according to the backend and the refresh route, for example, has a time limit after passing from expired to renewing the token, that would be more of a business rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we saw five ways to improve our communication with the backend and taking into account the best experience for the end user, there are many other approaches that can improve our backend call service, but just by implementing these concepts we will have better maintenance and usability of our system. In future posts we will see how to further improve this service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" alt="Obrigado até a próxima" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Axios - &lt;a href="https://axios-http.com/docs/intro" rel="noopener noreferrer"&gt;https://axios-http.com/docs/intro&lt;/a&gt;&lt;br&gt;
React - &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;https://reactjs.org/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>help</category>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>5 tips to improve integration with the backend, Angular and Rxjs</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Sun, 29 Jan 2023 14:53:13 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/5-tips-to-improve-integration-with-the-backend-angular-and-rxjs-44d9</link>
      <guid>https://forem.com/antoniojuniordev/5-tips-to-improve-integration-with-the-backend-angular-and-rxjs-44d9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Unlike react, we have many libraries to communicate with the backend as mentioned in this post &lt;a href="https://antoniojunior.dev/article/1065853" rel="noopener noreferrer"&gt;5 tips to improve integration with the backend, React with Axios&lt;/a&gt;, in angular it is recommended to use the HttpClient and the rxjs that come with it, of course we can still install an axios or use a fetch without problems.&lt;/p&gt;

&lt;p&gt;Even though angular has these points, it is important to think of points to help us with maintenance and better communication so as not to affect the usability of the user.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  1 - Encapsulate service
&lt;/h2&gt;

&lt;p&gt;We must create a generic service called with the library that we chose to use for the integration and simply just use it in the application, with the same idea of components like card, inputs, among others that we already do.&lt;/p&gt;

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

&lt;p&gt;First we have to create an angular service http-request.service.ts (Remembering you can put another name if you want) it will contain all the generic http methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(take(1));
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('post', url, { body: body });
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('get', url, { params: params });
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('put', url, { body: body });
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('delete', url, {});
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created an httpRequest function (already thinking about reuse) that is being used by all generic methods, as we will evolve it in the next topics.&lt;/p&gt;

&lt;p&gt;In the end we will have a folder structure like this.&lt;/p&gt;

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

&lt;p&gt;We will be able to use the service in our components this way.&lt;/p&gt;

&lt;p&gt;Following good practices, we created a service in our module where all calls to our components are located.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@angular/core';
import { HttpRequestService } from 'src/app/shared/http/http-request.service';

import { Authenticate } from '../model/authenticate';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(private httpRequest: HttpRequestService) {}

  login(user: Authenticate) {
    return this.httpRequest.post(`/authenticate`, user);
  }
}

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

&lt;/div&gt;



&lt;p&gt;Now just importing this service into our component and passing the data to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { AuthService } from '../service/auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  public loginForm!: FormGroup;
  public loading = false;
  public error = '';

  constructor(
    private _formBuilder: FormBuilder,
    private _authService: AuthService,
  ) {}

  ngOnInit(): void {
    this.loginForm = this._formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required]]
    });
  }

  handleLogin() {
    this.loading = true;
    this._authService.login(this.loginForm.value).subscribe({
    next: data =&amp;gt; {
        this.loginForm.reset();
        this.loading = false;
    },
    error: ({ error }) =&amp;gt; {
        this.loading = false;
    }
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But why is it necessary to use this approach?&lt;/p&gt;

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

&lt;p&gt;When we work with a generic service and only import it into our application, it becomes simpler to maintain and modify later. See how we can do it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Add headers to all requests
&lt;/h2&gt;

&lt;p&gt;Now let's configure headers for all our requests, we'll need this point to pass token among other information that your backend may need as a business rule.&lt;/p&gt;

&lt;p&gt;Let's create an interceptor file (remembering that you can put another name if you want) for this, as it is the best way to not just repeat code. See the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

interface Header {[key: string]: string}

@Injectable()
export class Intercept implements HttpInterceptor {
  intercept(httpRequest: HttpRequest&amp;lt;any&amp;gt;, next: HttpHandler): Observable&amp;lt;HttpEvent&amp;lt;any&amp;gt;&amp;gt; {
    if (httpRequest.headers.get('noHeader')) {
      const cloneReq = httpRequest.clone({
        headers: httpRequest.headers.delete('noHeader')
      });
      return next.handle(cloneReq);
    }

    const headers: Header = {
      Authorization: `Bearer ${localStorage.getItem('token') || ''}`
    };

    return next.handle(httpRequest.clone({ setHeaders: { ...headers } }));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's reference that interceptor in our appModule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './layouts/app/app.component';
import { Intercept } from './shared/http/intercept';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule,
    BrowserAnimationsModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: Intercept,
      multi: true
    },
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have already retrieved the localstorage token and added it to all calls to the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 - Redirect unauthorized or unauthenticated user
&lt;/h2&gt;

&lt;p&gt;We must have user redirection strategies when the user does not have authorization or permission so that he does not have the need to do this in our components.&lt;/p&gt;

&lt;p&gt;At this point, I'm going to leave the code ready for the redirection and the fourth topic for better use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { retryWhen, scan, take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private redirectUser(status: number): void {
    if (status === 401) {
      localStorage.clear();
      // User redirection rule for login page
      this.router.navigateByUrl('/login');
    }
    if (status === 403) {
      // User redirect rule to not allowed page
      this.router.navigateByUrl('/');
    }
  }

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(
      retryWhen(e =&amp;gt;
        e.pipe(
          scan((errorCount, error) =&amp;gt; {
            this.redirectUser(error.status);
            if (errorCount &amp;gt;= environment.retryAttempts || error.status &amp;lt; 500) throw error;
            return errorCount + 1;
          }, 0)
        )
      ),
      take(1)
    );
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('post', url, { body: body });
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('get', url, { params: params });
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('put', url, { body: body });
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('delete', url, {});
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave open where you want to send the user for both 401(Unauthenticated) and 403(Unauthorized). That way, even if the user manages to access a page that he couldn't, when the backend request comes back with the status code, the system will already direct him, this approach also works for when the token expires, which we'll see how to deal with later.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Request retry pattern
&lt;/h2&gt;

&lt;p&gt;Now we will need to apply a pattern retry to our requests so that our end user does not suffer from instabilities in the application as it may be undergoing a deploy or auto scaling of the infrastructure at the time of the call. For this we define a number of attempts in case the system returns error 500 or higher. Example showing no.&lt;/p&gt;

&lt;p&gt;With the help of the rxjs operators we can do a retryWhen as many times as configured in the retryAttempts environment every time the request has a status code greater than 500.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 - Refresh token
&lt;/h2&gt;

&lt;p&gt;When we work with authentication, it is a good practice and security rule to have tokens that expire so that the user does not stay logged in forever even without using the application.&lt;/p&gt;

&lt;p&gt;However, we have to solve the problem that if the token expires when the user cannot simply ask him to log in again, for that we have routes to refresh token.&lt;/p&gt;

&lt;p&gt;We can improve our http-request.service.ts service so that it does this automatically when calling routes from your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { retryWhen, scan, take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

interface Refresh {
  token: string;
  expired: number;
}

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private refreshToken(httpRequest: Observable&amp;lt;any&amp;gt;): Observable&amp;lt;any&amp;gt; {
    const value: number = Number(localStorage.getItem('expired'));
    if (value &amp;amp;&amp;amp; new Date(value) &amp;lt; new Date()) {
      this.refresh().subscribe(data =&amp;gt; {
        localStorage.setItem('token', data.token);
        localStorage.setItem('expired', String(new Date().setSeconds(data.expired)));
        return httpRequest;
      });
    }
    return httpRequest;
  }

  private refresh(): Observable&amp;lt;Refresh&amp;gt; {
    return this.http.get&amp;lt;Refresh&amp;gt;(`${environment.url}/refresh `).pipe(take(1));
  }

  private notAuthorization(status: number): void {
    if (status === 401) {
      localStorage.clear();
      this.router.navigateByUrl('/login');
    }
  }

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(
      retryWhen(e =&amp;gt;
        e.pipe(
          scan((errorCount, error) =&amp;gt; {
            this.notAuthorization(error.status);
            if (errorCount &amp;gt;= environment.retryAttempts || error.status &amp;lt; 500) throw error;
            return errorCount + 1;
          }, 0)
        )
      ),
      take(1)
    );
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('post', url, { body: body }));
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('get', url, { params: params }));
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('put', url, { body: body }));
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('delete', url, {}));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created a refreshToken() function that receives an Observable and returns the same, but it would check if the token's expired has already passed, and if so, make a new call to the backend, renewing the token and the expired. Remembering that this logic works according to the backend and the refresh route, for example, has a time limit after passing from expired to renewing the token, that would be more of a business rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we saw five ways to improve our communication with the backend and taking into account the best experience for the end user, there are many other approaches that can improve our backend call service, but just by implementing these concepts we will have better maintenance and usability of our system. In future posts we will see how to further improve this service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" alt="Obrigado até a próxima" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To the next&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Angular - &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;https://angular.io/&lt;/a&gt;&lt;br&gt;
Rxjs - &lt;a href="https://rxjs.dev" rel="noopener noreferrer"&gt;https://rxjs.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>5 dicas para melhorar integração com o backend, Angular e Rxjs</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Sun, 24 Apr 2022 23:52:11 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/melhorando-comunicacao-com-o-backend-angular-e-rxjs-18fc</link>
      <guid>https://forem.com/antoniojuniordev/melhorando-comunicacao-com-o-backend-angular-e-rxjs-18fc</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Diferente do react que temos muitas bibliotecas para realizar comunicação com o backend como foi mencionado nesse post &lt;a href="https://antoniojunior.dev/article/1065853"&gt;5 dicas para melhorar integração com o backend, React com Axios&lt;/a&gt;, no angular é recomendado utilizar o HttpClient e o rxjs que já vem com ele, isso é claro que ainda podemos instalar um axios ou usar um fetch sem problemas.&lt;/p&gt;

&lt;p&gt;Mesmo que no angular tenha esses pontos é importante pensar em pontos para nos ajudar na manutenção e uma melhor comunicação para não afetar a usabilidade do usuário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dv8Hp9kA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwd2nmaee5xylbn09w7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dv8Hp9kA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwd2nmaee5xylbn09w7u.png" alt="Angular e rxjs" width="880" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Encapsular serviço
&lt;/h2&gt;

&lt;p&gt;Devemos criar um serviço genérico de chamada com a biblioteca que escolhemos usar para fazer a integração e simplesmente só utilizar o mesmo na aplicação, com um tudo a mesma ideia de componentes como card, inputs, entre outros que já fazemos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/qYytk8KxBPA1q/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/qYytk8KxBPA1q/giphy.gif" alt="Serviço" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primeiro temos que criar um serviço do angular http-request.service.ts (Lembrando você pode colocar outro nome caso queira) ele irá conter todos os métodos http genéricos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(take(1));
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('post', url, { body: body });
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('get', url, { params: params });
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('put', url, { body: body });
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('delete', url, {});
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criamos uma função httpRequest (já pensando em reuso) que está sendo usada por todos os métodos genéricos, pois vamos evoluir ela nos próximo tópicos.&lt;/p&gt;

&lt;p&gt;No final teremos uma estrutura de pasta assim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qbo0om_M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wlk10nhaudh8svf28yx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qbo0om_M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wlk10nhaudh8svf28yx.png" alt="Estrutura de pasta" width="823" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos poder usar o serviço em nossos componentes dessa maneira.&lt;/p&gt;

&lt;p&gt;Seguindo as boas práticas criamos um serviço em nosso em nosso módulo onde ficar todas as chamadas de nosso componentes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@angular/core';
import { HttpRequestService } from 'src/app/shared/http/http-request.service';

import { Authenticate } from '../model/authenticate';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(private httpRequest: HttpRequestService) {}

  login(user: Authenticate) {
    return this.httpRequest.post(`/authenticate`, user);
  }
}

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

&lt;/div&gt;



&lt;p&gt;Agora só importando esse serviço em nosso componente e passando os dados para o mesmo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { AuthService } from '../service/auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  public loginForm!: FormGroup;
  public loading = false;
  public error = '';

  constructor(
    private _formBuilder: FormBuilder,
    private _authService: AuthService,
  ) {}

  ngOnInit(): void {
    this.loginForm = this._formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required]]
    });
  }

  handleLogin() {
    this.loading = true;
    this._authService.login(this.loginForm.value).subscribe({
    next: data =&amp;gt; {
        this.loginForm.reset();
        this.loading = false;
    },
    error: ({ error }) =&amp;gt; {
        this.loading = false;
    }
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas porque se faz necessário utilizar essa abordagem?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/CaiVJuZGvR8HK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/CaiVJuZGvR8HK/giphy.gif" alt="Porque devemos usar" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quando trabalhamos com serviço genérico e só importamos ele em nossa aplicação fica mais simples de realizar manutenção e de modificar posteriormente. Veja como podemos fazer abaixo.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Adicionar headers a todas request
&lt;/h2&gt;

&lt;p&gt;Agora vamos configurar headers a todas nossas request, vamos necessitar desse ponto para passar token entre outras informações que seu backend pode precisar como regra de negócio.&lt;/p&gt;

&lt;p&gt;Vamos criar um arquivo interceptor (Lembrando você pode colocar outro nome caso queira) para isso, pois é a melhor maneira para não ficarmos só repetindo código. Veja o exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

interface Header {[key: string]: string}

@Injectable()
export class Intercept implements HttpInterceptor {
  intercept(httpRequest: HttpRequest&amp;lt;any&amp;gt;, next: HttpHandler): Observable&amp;lt;HttpEvent&amp;lt;any&amp;gt;&amp;gt; {
    if (httpRequest.headers.get('noHeader')) {
      const cloneReq = httpRequest.clone({
        headers: httpRequest.headers.delete('noHeader')
      });
      return next.handle(cloneReq);
    }

    const headers: Header = {
      Authorization: `Bearer ${localStorage.getItem('token') || ''}`
    };

    return next.handle(httpRequest.clone({ setHeaders: { ...headers } }));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E vamos referência esse interceptor em nosso appModule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './layouts/app/app.component';
import { Intercept } from './shared/http/intercept';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule,
    BrowserAnimationsModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: Intercept,
      multi: true
    },
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui já recuperamos o token do localstorage e adicionamos a todas as chamadas ao backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 - Redirecionamento usuário não autorizado ou não autenticado
&lt;/h2&gt;

&lt;p&gt;Devemos ter estratégias de redirecionamento de usuário quando o mesmo não tiver autorização ou permissão para que não tenha a necessidade de fazer isso em nossos componentes.&lt;/p&gt;

&lt;p&gt;Nesse ponto já vou deixar o código pronto do redirecionamento e do quarto tópico para melhor aproveitamento.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { retryWhen, scan, take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private redirectUser(status: number): void {
    if (status === 401) {
      localStorage.clear();
      // Regra de redirecionamento de usuário para página de login
      this.router.navigateByUrl('/login');
    }
    if (status === 403) {
      // Regra de redirecionamento de usuário para página de não permitido
      this.router.navigateByUrl('/');
    }
  }

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(
      retryWhen(e =&amp;gt;
        e.pipe(
          scan((errorCount, error) =&amp;gt; {
            this.redirectUser(error.status);
            if (errorCount &amp;gt;= environment.retryAttempts || error.status &amp;lt; 500) throw error;
            return errorCount + 1;
          }, 0)
        )
      ),
      take(1)
    );
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('post', url, { body: body });
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('get', url, { params: params });
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('put', url, { body: body });
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.httpRequest('delete', url, {});
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deixe aberto para onde você deseja mandar o usuário tanto para 401(Não autenticado) e 403 (Não autorizado). Dessa maneira mesmo que o usuário conseguir acessar uma página que não poderia quando a request do backend voltar com o status code o sistema já irá direcioná-lo,  essa abordagem serve também para quando token expira, que vamos ver como lidar com isso mais adiante.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Pattern retry de request
&lt;/h2&gt;

&lt;p&gt;Agora vamos precisar aplicar ​​pattern retry em nossas requests para que o nosso usuário final não sofra com instabilidades na aplicação pois a mesma pode está passando por um deploy ou auto scaling da infraestrutura no momento da chamada. Para isso nós definimos um número de tentativas caso o sistema retorne erro 500 ou superior. Exemplo mostrando no.&lt;/p&gt;

&lt;p&gt;Com o auxílio do operators do rxjs podemos fazer um ​​retryWhen quantas vezes for configurado na environment retryAttempts toda vez que a request tenha status code maior igual a 500.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 - Refresh token
&lt;/h2&gt;

&lt;p&gt;Quando trabalhamos com autenticação é uma boa prática e regra de segurança termos tokens que expiram para que o usuário não fique logado para sempre mesmo sem usar a aplicação.&lt;/p&gt;

&lt;p&gt;Porém temos que resolver o problema que se o token expirado quando o usuário não podemos simplesmente pedir para que o mesmo realize login novamente, para isso temos rotas para refresh token.&lt;/p&gt;

&lt;p&gt;Podemos melhorar nosso serviço http-request.service.ts para que ele faça isso automaticamente durante as chamadas das rotas da sua aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { retryWhen, scan, take } from 'rxjs/operators';
import { environment } from 'src/environments/environment';

interface Refresh {
  token: string;
  expired: number;
}

@Injectable({
  providedIn: 'root'
})
export class HttpRequestService {
  private uri = `${environment.url}`;

  constructor(private http: HttpClient, private router: Router) {}

  private refreshToken(httpRequest: Observable&amp;lt;any&amp;gt;): Observable&amp;lt;any&amp;gt; {
    const value: number = Number(localStorage.getItem('expired'));
    if (value &amp;amp;&amp;amp; new Date(value) &amp;lt; new Date()) {
      this.refresh().subscribe(data =&amp;gt; {
        localStorage.setItem('token', data.token);
        localStorage.setItem('expired', String(new Date().setSeconds(data.expired)));
        return httpRequest;
      });
    }
    return httpRequest;
  }

  private refresh(): Observable&amp;lt;Refresh&amp;gt; {
    return this.http.get&amp;lt;Refresh&amp;gt;(`${environment.url}/refresh `).pipe(take(1));
  }

  private notAuthorization(status: number): void {
    if (status === 401) {
      localStorage.clear();
      this.router.navigateByUrl('/login');
    }
  }

  private httpRequest&amp;lt;Data&amp;gt;(method: string, url: string, options: any): Observable&amp;lt;any&amp;gt; {
    return this.http.request(method, `${this.uri}${url}`, { ...options }).pipe(
      retryWhen(e =&amp;gt;
        e.pipe(
          scan((errorCount, error) =&amp;gt; {
            this.notAuthorization(error.status);
            if (errorCount &amp;gt;= environment.retryAttempts || error.status &amp;lt; 500) throw error;
            return errorCount + 1;
          }, 0)
        )
      ),
      take(1)
    );
  }

  post&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('post', url, { body: body }));
  }

  get&amp;lt;Data&amp;gt;(url: string, params?: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('get', url, { params: params }));
  }

  put&amp;lt;Data&amp;gt;(url: string, body: any): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('put', url, { body: body }));
  }

  delete&amp;lt;Data&amp;gt;(url: string): Observable&amp;lt;any&amp;gt; {
    return this.refreshToken(this.httpRequest('delete', url, {}));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criamos uma função de refreshToken() que recebe um Observable e retorna o mesmo porém ela iria verificar se expired do token já passou, e se sim já realizar uma nova chamada ao backend renovando o token e o expired. Lembrando essa lógica funciona de acordo com o backend e a rota de refresh por exemplo tem um tempo limite depois que passar do expired para se renovar o token ai seria mais regra de negócio. &lt;/p&gt;

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

&lt;p&gt;Nesse post vimos cinco maneiras de melhorar nossa comunicação com o backend e levando em conta a melhor experiência para o usuário final, existem muitas outras abordagens que podem melhorar o nosso serviço de chamada ao backend, porém só em implementar esses conceitos já teremos uma melhor manutenção e usabilidade do nosso sistema. Em posts futuros veremos como melhorar ainda mais esse serviço.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ICOgUNjpvO0PC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ICOgUNjpvO0PC/giphy.gif" alt="Obrigado até a próxima" width="400" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Até a próxima&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Angular - &lt;a href="https://angular.io/"&gt;https://angular.io/&lt;/a&gt;&lt;br&gt;
Rxjs - &lt;a href="https://rxjs.dev"&gt;https://rxjs.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>webdev</category>
      <category>português</category>
    </item>
    <item>
      <title>5 dicas para melhorar integração com o backend, React com Axios</title>
      <dc:creator>Junior</dc:creator>
      <pubDate>Sun, 24 Apr 2022 22:15:28 +0000</pubDate>
      <link>https://forem.com/antoniojuniordev/melhorando-comunicacao-com-o-backend-react-com-axios-2kg5</link>
      <guid>https://forem.com/antoniojuniordev/melhorando-comunicacao-com-o-backend-react-com-axios-2kg5</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Na maior parte das aplicações frontend temos que fazer integrações com o backend e com isso vem várias bibliotecas que podemos usar como por exemplo fetch, ajax, axios entre outras e cada uma tem suas características, vantagens e desvantagens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99dh3eb9oews0mbisou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99dh3eb9oews0mbisou.png" alt="Bibliotecas de integrações com backend"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mas independente de qual vamos utilizar em nossa aplicação temos que pensar em pontos para nos ajudar na manutenção e uma melhor comunicação para não afetar a usabilidade do usuário.&lt;/p&gt;

&lt;p&gt;Nesse post vou utilizar o axios com o react e aplicando conceitos que considero de extrema importância que devemos abordar em nossas aplicações. Vou levar em consideração que você já tem um projeto react com o axios instalado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgeuh8vey6x62vuavporf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgeuh8vey6x62vuavporf.jpeg" alt="Logo axios"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Encapsular serviço
&lt;/h2&gt;

&lt;p&gt;Devemos criar um serviço genérico de chamada com a biblioteca que escolhemos usar para fazer a integração e simplesmente só utilizar o mesmo na aplicação, com um tudo a mesma ideia de componentes como card, inputs, entre outros que já fazemos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/qYytk8KxBPA1q/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/qYytk8KxBPA1q/giphy.gif" alt="Serviço"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primeiro temos que criar um arquivo http.js ou http.ts (Lembrando você pode colocar outro nome caso queira) para exportar o axios com a base url do nosso backend já configurada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora devemos criar um outro arquivo index.js ou index.ts aonde vamos exportar os métodos comumente mais usados do http já envolvendo em um blog try catch para lidarmos com os erros das chamadas aqui mesmo. Aqui já estamos utilizando o arquivo que criamos acima do http.ts para acionar o axios com os parâmetros, em posts futuros vamos evoluir esse arquivo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import http from './http';

export default {
  async get(url: string) {
    try {
      const response = await http.get(url);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async post(url: string, send: object) {
    try {
      const response = await http.post(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async put(url: string, send: object) {
    try {
      const response = await http.put(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async delete(url: string) {
    try {
      await http.delete(url);
      return true;
    } catch (err: any) {
      return false;
    }
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No final teremos uma estrutura de pasta assim.&lt;/p&gt;

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

&lt;p&gt;Vamos poder invocar o método em nossos componentes dessa maneira.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await services.post( '/authenticate', { email, password } );&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Mas porque se faz necessário utilizar essa abordagem?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/CaiVJuZGvR8HK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/CaiVJuZGvR8HK/giphy.gif" alt="Porque devemos usar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quando trabalhamos com serviço genérico e só importamos ele em nossa aplicação fica mais simples de realizar manutenção e de modificar posteriormente. Veja como podemos fazer abaixo.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Adicionar headers a todas request
&lt;/h2&gt;

&lt;p&gt;Agora vamos configurar headers a todas nossas request, vamos necessitar desse ponto para passar token entre outras informações que seu backend pode precisar como regra de negócio.&lt;/p&gt;

&lt;p&gt;Vamos criar um interceptors do axios para isso, pois é a melhor maneira para não ficarmos só repetindo código. Veja o exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui já recuperamos o token do localstorage e adicionamos a todas as chamadas ao backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 - Redirecionamento usuário não autorizado ou não autenticado
&lt;/h2&gt;

&lt;p&gt;Devemos ter estratégias de redirecionamento de usuário quando o mesmo não tiver autorização ou permissão para que não tenha a necessidade de fazer isso em nossos componentes.&lt;/p&gt;

&lt;p&gt;Para isso devemos criar outro interceptors para lidar com esse processo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

http.interceptors.response.use(
  (value) =&amp;gt; {
    return Promise.resolve(value);
  },
  (error) =&amp;gt; {
    const { isAxiosError = false, response = null } = error;

    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 401) {
      // Regra de redirecionamento de usuário para página de login
      return Promise.reject(error);
    }
    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 403) {
      // Regra de redirecionamento de usuário para página de não permitido
      return Promise.reject(error);
    }
    return Promise.reject(error);
  }
);

export default http;

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

&lt;/div&gt;



&lt;p&gt;Deixe aberto para onde você deseja mandar o usuário tanto para 401(Não autenticado) e 403 (Não autorizado). Dessa maneira mesmo que o usuário conseguir acessar uma página que não poderia quando a request do backend voltar com o status code o sistema já irá direcioná-lo,  essa abordagem serve também para quando token expira, que vamos ver como lidar com isso mais adiante.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Pattern retry de request
&lt;/h2&gt;

&lt;p&gt;Agora vamos precisar aplicar ​​pattern retry em nossas requests para que o nosso usuário final não sofra com instabilidades na aplicação pois a mesma pode está passando por um deploy ou auto scaling da infraestrutura no momento da chamada. Para isso nós definimos um número de tentativas caso o sistema retorne erro 500 ou superior. Exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Axios, { AxiosRequestConfig } from 'axios';

const http = Axios.create({
  baseURL: process.env.REACT_APP_URL,
});

http.interceptors.request.use((config: AxiosRequestConfig) =&amp;gt; {
  const token = window.localStorage.getItem('token');
  if (!token) return config;
  if (config?.headers) {
    config.headers = { Authorization: `Bearer ${token}` };
  }
  return config;
});

http.interceptors.response.use(
  (value) =&amp;gt; {
    return Promise.resolve(value);
  },
  (error) =&amp;gt; {
    const { isAxiosError = false, response = null } = error;

    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 401) {
      // Regra de redirecionamento de usuário para página de login
      return Promise.reject(error);
    }
    if (isAxiosError &amp;amp;&amp;amp; response &amp;amp;&amp;amp; response.status === 403) {
      // Regra de redirecionamento de usuário para página de não permitido
      return Promise.reject(error);
    }
    return Promise.reject(error);
  }
);

let counter = 1;

http.interceptors.response.use(
  (response) =&amp;gt; {
    return response;
  },
  (error) =&amp;gt; {
    if (
      error.response.status &amp;gt;= 500 &amp;amp;&amp;amp;
      counter &amp;lt; Number(process.env.REACT_APP_RETRY)
    ) {
      counter++;
      return http.request(error.config);
    }
    counter = 1;
    return Promise.reject(error);
  }
);

export default http;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Foi criando um interceptors para que tenha retentativa de acordo com o número que for definido em &lt;strong&gt;process.env.REACT_APP_RETRY&lt;/strong&gt; vezes quando a request tenha status code maior igual a 500.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 - Refresh token
&lt;/h2&gt;

&lt;p&gt;Quando trabalhamos com autenticação é uma boa prática e regra de segurança termos tokens que expiram para que o usuário não fique logado para sempre mesmo sem usar a aplicação.&lt;/p&gt;

&lt;p&gt;Porém temos que resolver o problema que se o token expirado quando o usuário não podemos simplesmente pedir para que o mesmo realize login novamente, para isso temos rotas para refresh token.&lt;/p&gt;

&lt;p&gt;Podemos melhorar nosso arquivo index.ts para que ele faça isso automaticamente durante as chamadas das rotas da sua aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import http from './http';

async function refreshToken() {
  const value = Number(localStorage.getItem('expired'));
  if (value &amp;amp;&amp;amp; new Date(value) &amp;lt; new Date()) {
    const result = await http.get('/refresh');
    localStorage.setItem('token', result.data.token);
    localStorage.setItem(
      'expired',
      String(new Date().setSeconds(result.data.expired))
    );
  }
}

export default {
  async get(url: string) {
    try {
      await refreshToken();
      const response = await http.get(url);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async post(url: string, send: object) {
    try {
      await refreshToken();
      const response = await http.post(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async put(url: string, send: object) {
    try {
      await refreshToken();
      const response = await http.put(url, send);
      return response;
    } catch (err: any) {
      return false;
    }
  },
  async delete(url: string) {
    try {
      await refreshToken();
      await http.delete(url);
      return true;
    } catch (err: any) {
      return false;
    }
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criamos uma função de refreshToken() que sempre será chamada antes de todas as chamadas da nossa aplicação ela iria verificar se expired do token já passou, e se sim já realizar uma nova chamada ao backend renovando o token e o expired. Lembrando essa lógica funciona de acordo com o backend e a rota de refresh por exemplo tem um tempo limite depois que passar do expired para se renovar o token ai seria mais regra de negócio. &lt;/p&gt;

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

&lt;p&gt;Nesse post vimos cinco maneiras de melhorar nossa comunicação com o backend e levando em conta a melhor experiência para o usuário final, existem muitas outras abordagens que podem melhorar o nosso serviço de chamada ao backend, porém só em implementar esses conceitos já teremos uma melhor manutenção e usabilidade do nosso sistema. Em posts futuros veremos como melhorar ainda mais esse serviço.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdt3anp6jew966zdx7p5.gif" alt="Obrigado até a próxima"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Axios - &lt;a href="https://axios-http.com/docs/intro" rel="noopener noreferrer"&gt;https://axios-http.com/docs/intro&lt;/a&gt;&lt;br&gt;
React - &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;https://reactjs.org/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>axios</category>
      <category>português</category>
    </item>
  </channel>
</rss>
