Guia completo de otimização de APIs no Next.js: cache, streaming e Edge Functions na prática

Numa sexta-feira, às nove da noite, o gerente de produto mandou uma captura de tela no grupo. No vídeo de teste com usuários, a pessoa abria a lista do blog pelo celular, o ícone de Loading girava por 5 segundos inteiros e a página continuava branca. No canto inferior, alguém soltou: “Que tipo de site é esse em pleno século XXI?”
Abri o Chrome DevTools e veio o susto: a requisição da API levava 3200ms. Na hora deu aquele aperto. Eu já sabia que o endpoint era lento, mas vinha empurrando a otimização. Não imaginava que estava tão ruim.
Depois passei dois dias estudando otimização de desempenho no Next.js e percebi que a solução não era tão complicada. Com a estratégia certa de cache, mais resposta em streaming e edge computing, o tempo de resposta caiu direto de 3 segundos para menos de 500ms. Mais importante: eu entendi quando usar cada abordagem. Isso vale muito mais do que só conhecer as técnicas.
Hoje vamos falar dessas três frentes: como escolher cache, como implementar streaming e em quais cenários Edge Functions fazem sentido. O código aqui foi rodado de verdade, e os números de desempenho também vêm de teste real. Dá para pegar e aplicar.
Por que sua API no Next.js está tão lenta?
Vamos começar pelos gargalos mais comuns. Quando investiguei aquele endpoint de 3 segundos, encontrei alguns problemas bem típicos:
Consulta ao banco sem otimização. O código tinha um loop em que cada post buscava as informações do autor separadamente: o clássico problema de N+1 queries. Com 100 posts, eram 100 requisições ao banco. Como isso não ficaria lento? Para piorar, algumas tabelas nem tinham índice.
Zero cache. A cada atualização da página, o servidor consultava o banco, recalculava tudo e formatava a resposta de novo. Algumas configurações mudavam uma vez por mês, mas eram recalculadas várias vezes por segundo.
Tudo voltava de uma vez. O endpoint retornava o conteúdo completo de 100 posts, incluindo o corpo dos artigos. A resposta JSON passava de 2MB, e só a transferência pela rede já consumia 1 segundo. A página de lista nem precisava do corpo completo; título e resumo bastavam.
Localização do servidor. Nosso servidor estava na costa oeste dos Estados Unidos. Para usuários na China, uma ida e volta já começava em 200ms, sem contar o impacto do GFW… melhor nem entrar nesse assunto.
A mudança de cache trazida pelo Next.js 16
Em outubro de 2025, o Next.js 16 trouxe uma mudança importante: saiu do cache implícito para o cache explícito.
Antes, o Next.js tentava cachear muita coisa automaticamente. Parecia conveniente, mas na prática gerava confusão: isso foi cacheado ou não? Por quanto tempo? Como limpo? Muitas vezes os dados já tinham sido atualizados, mas a página continuava mostrando a versão antiga. Depois de meia hora depurando, você descobria que o culpado era o cache.
Agora você precisa dizer explicitamente ao Next.js o que cachear e por quanto tempo. Dá um pouco mais de trabalho, mas pelo menos você sabe o que está acontecendo. O controle fica muito melhor.
Três direções para otimizar desempenho
Depois de entender o problema, os caminhos ficaram claros:
- Cache: não repita trabalho que já foi feito.
- Resposta em streaming: calcule e envie aos poucos; não espere tudo ficar pronto para responder.
- Edge computing: leve o servidor para mais perto do usuário.
Vamos por partes.
Estratégia de cache: escolher certo já resolve metade do problema
O Next.js tem quatro mecanismos de cache: Request Memoization, Data Cache, Full Route Cache e Router Cache. Na primeira vez que li a documentação, também fiquei perdido. Como memorizar tudo isso?
Na prática, você não precisa decorar todos. Para API Routes, o mais comum é usar Data Cache, guardando o resultado de uma consulta ao banco ou a resposta de uma API externa.
Cenário 1: cache de dados estáticos
Configurações do site e listas de categorias quase não mudam. Dá para cachear por algumas horas sem medo.
// app/api/categories/route.js
export async function GET() {
const data = await fetch('https://api.example.com/categories', {
next: { revalidate: 3600 } // cache por 1 hora
})
return Response.json(await data.json())
}
É só isso. revalidate: 3600 significa cache por 1 hora, com atualização automática depois desse período.
Cenário 2: cache de dados relacionados ao usuário
Dados como perfil do usuário não mudam o tempo todo, mas também não podem ficar antigos para sempre. Nesses casos, stale-while-revalidate funciona bem:
// app/api/user/profile/route.js
export async function GET(request) {
const user = await getUserFromDB()
return new Response(JSON.stringify(user), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 's-maxage=60, stale-while-revalidate=300'
}
})
}
A ideia é esperta: primeiro devolve os dados em cache, mesmo que possam estar um pouco antigos, e atualiza o cache de forma assíncrona em segundo plano. O usuário não sente a latência, e os dados não ficam velhos demais.
s-maxage=60 indica que o cache fica fresco por 60 segundos. stale-while-revalidate=300 indica que, depois de expirar, os dados antigos ainda podem ser usados por 300 segundos enquanto o cache é atualizado em segundo plano.
Cenário 3: dados em tempo real não devem usar cache
Preço de ações e mensagens de chat exigem atualização imediata. Nesses casos, não use cache. Ou você não cacheia nada, ou usa WebSocket ou Server-Sent Events para enviar atualizações.
export async function GET() {
const price = await getStockPrice()
return new Response(JSON.stringify(price), {
headers: {
'Cache-Control': 'no-store' // sem cache
}
})
}
Invalidação de cache: o que fazer depois que os dados mudam?
O usuário atualizou o perfil, mas o cache ainda mostra a versão antiga? Aí você precisa limpar o cache manualmente.
O Next.js oferece duas APIs para isso: revalidateTag e revalidatePath.
// app/api/user/update/route.js
import { revalidateTag } from 'next/cache'
export async function POST(request) {
const data = await request.json()
await updateUserProfile(data)
// Limpa o cache relacionado ao usuário
revalidateTag('user-profile')
return Response.json({ success: true })
}
No endpoint de consulta, marque o cache com uma tag:
export async function GET() {
const data = await fetch('db-api/user', {
next: {
revalidate: 3600,
tags: ['user-profile'] // tag
}
})
return Response.json(await data.json())
}
Assim, depois que o perfil é atualizado, o cache relacionado expira imediatamente. Na próxima requisição, o usuário recebe os dados novos.
Pegadinhas comuns
Pegadinha 1: cache em excesso. Já vi gente cachear status de pedido por 1 hora. Resultado: depois do pagamento, o usuário passava um bom tempo sem ver o status atualizado. O tempo de cache precisa acompanhar a natureza dos dados. Não é quanto mais longo, melhor.
Pegadinha 2: esquecer o aquecimento do cache. A primeira requisição ainda pode ser lenta, porque o cache está vazio. Você pode chamar os endpoints principais logo após o deploy e carregar os dados mais acessados antes dos usuários chegarem.
Pegadinha 3: cache key mal desenhada. Se os dados do usuário A entram no cache e o usuário B recebe esses mesmos dados, o problema é sério. Garanta que a cache key inclua identificadores como o ID do usuário.
Resposta em streaming: listas grandes sem travar a experiência
Cache resolve trabalho repetido, mas alguns dados continuam demorados de calcular ou grandes demais para enviar de uma vez. É aí que resposta em streaming entra.
O que é resposta em streaming?
Uma resposta tradicional de API é como ir a um restaurante e esperar que o cozinheiro prepare todos os pratos antes de servir a mesa. Se você pediu 10 pratos, precisa esperar o mais demorado ficar pronto.
Streaming é diferente: ficou pronto, serviu. Você começa a comer enquanto os próximos pratos ainda estão vindo. O tempo total pode até ser parecido, mas a experiência muda completamente, porque você não fica parado esperando.
Para o usuário, isso troca “olhar para uma tela branca por 3 segundos” por “ver os primeiros itens em 500ms e já começar a navegar”. A sensação é outra.
Quando usar resposta em streaming?
Alguns cenários aparecem o tempo todo:
- Listas longas: produtos, posts, resultados de busca.
- Conteúdo gerado por IA: aquele efeito de digitação do ChatGPT é, na prática, streaming.
- Processamento de arquivos grandes: exportar Excel, gerar PDF.
- Logs em tempo real: logs de build, progresso de tarefas.
Em geral, se há muito dado ou cálculo demorado, vale considerar streaming.
Como implementar no Next.js?
A forma mais comum é usar ReadableStream:
// app/api/posts/stream/route.js
export async function GET() {
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
// Busca os dados em lotes
for (let page = 0; page < 5; page++) {
// Consulta 20 itens por vez
const posts = await fetchPostsFromDB({ page, limit: 20 })
// Envia este lote de dados
const chunk = JSON.stringify(posts) + '\n'
controller.enqueue(encoder.encode(chunk))
// Simula tempo de processamento
await new Promise(r => setTimeout(r, 100))
}
// Finaliza o envio dos dados
controller.close()
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked'
}
})
}
O código não é complicado. O núcleo é:
- Criar um
ReadableStream. - Buscar os dados em lotes dentro do método
start. - Enviar cada lote com
controller.enqueue(). - Chamar
controller.close()quando tudo terminar.
Como receber no frontend?
Se o backend envia dados em streaming, o frontend também precisa tratar a resposta:
async function fetchStreamData() {
const response = await fetch('/api/posts/stream')
const reader = response.body.getReader()
const decoder = new TextDecoder()
let allPosts = []
while (true) {
const { done, value } = await reader.read()
if (done) {
console.log('Dados recebidos com sucesso')
break
}
// Decodifica os dados
const chunk = decoder.decode(value)
// Faz o parse do JSON, um item por linha
const posts = JSON.parse(chunk)
allPosts = [...allPosts, ...posts]
// Atualiza a UI em tempo real
updatePostList(allPosts)
}
}
Quando o usuário abre a página, a lista aparece aos poucos em vez de ficar em branco por vários segundos.
Comparação do efeito real
Depois que adicionei streaming à lista do blog:
O tempo total só caiu 1300ms, mas a velocidade percebida mais que dobrou. Com 500ms, a pessoa já interage; o restante do tempo é gasto navegando, não esperando.
Um truque pequeno
Se o volume de dados for muito grande, combine streaming com Virtual Scrolling. O frontend renderiza apenas a área visível e deixa o restante fora da tela. Assim, mesmo recebendo 1000 itens, a página não trava.
Em React, dá para usar react-window ou react-virtualized. Em Vue, vue-virtual-scroller resolve bem.
Edge Functions: leve a API para perto do usuário
Cache e streaming atuam na camada de software, mas existe uma solução ainda mais direta: levar o servidor para mais perto do usuário.
O impacto da distância física
A latência de rede vem principalmente da distância física. A velocidade da luz é limitada. Um pacote saindo de Pequim até a costa oeste dos Estados Unidos leva pelo menos 200ms para ir e voltar. É física, não dá para otimizar.
Antes, a gente só conseguia colocar o servidor em um ponto fixo, como Alibaba Cloud em Pequim. Usuários de Pequim acessavam rápido; usuários dos Estados Unidos sofriam.
A ideia das Edge Functions é simples: implantar o código em dezenas ou até centenas de nós pelo mundo. Quando o usuário acessa, a requisição é roteada automaticamente para o nó mais próximo. Usuários em Pequim acessam um nó em Pequim; usuários em Nova York acessam um nó em Nova York. A latência pode cair para menos de 50ms.
Diferença entre Edge Runtime e Node.js Runtime
As API Routes do Next.js rodam por padrão no Node.js Runtime. Ali você pode usar todas as APIs do Node.js, como fs, crypto e conexões com banco de dados.
O Edge Runtime é diferente. Ele se baseia no motor V8, o mesmo do Chrome, e não é um ambiente Node.js completo. A vantagem é a inicialização muito rápida, de 0 a 5ms. A desvantagem é que muitas APIs do Node.js não estão disponíveis.
Uma comparação simples:
| Característica | Node.js Runtime | Edge Runtime |
|---|---|---|
| Velocidade de inicialização | 100-500ms | 0-5ms |
| APIs disponíveis | Todas as APIs do Node.js | Limitado a APIs padrão da Web |
| Cenários indicados | Lógica de negócio complexa, operações no banco | Lógica leve, autenticação, proxy |
| Latência global | Depende do local do deploy | Global <50ms |
| Limite de memória | Mais alto | Mais baixo (128MB) |
Quais cenários combinam com Edge Functions?
Nem toda API deve migrar para Edge. Estes são os casos em que ela costuma fazer mais sentido:
Cenário 1: autenticação
O melhor caso para Edge é autenticação. Checar JWT token e validar API key são operações leves. Faça isso na borda e as requisições inválidas nem chegam ao servidor central.
// app/api/auth/route.js
export const runtime = 'edge'
export async function GET(request) {
const token = request.headers.get('authorization')
if (!token) {
return new Response('Não autorizado', { status: 401 })
}
// Valida o token, usando uma biblioteca como jose, compatível com Edge
const isValid = await verifyToken(token)
if (!isValid) {
return new Response('Token inválido', { status: 401 })
}
return Response.json({ user: 'autenticado' })
}
Cenário 2: personalização por geolocalização
Retorne conteúdo diferente com base no IP do usuário, como idioma, moeda ou recomendações.
export const runtime = 'edge'
export async function GET(request) {
// Obtém a localização do usuário, injetada automaticamente pela Vercel
const country = request.geo?.country || 'US'
const city = request.geo?.city || 'Unknown'
// Retorna conteúdo diferente conforme a localização
const content = getLocalizedContent(country)
return Response.json({
country,
city,
content,
currency: country === 'CN' ? 'CNY' : 'USD'
})
}
Não precisa consultar o banco. A borda processa tudo diretamente, e a resposta sai muito rápido.
Cenário 3: proxy de API
Às vezes o frontend precisa chamar várias APIs externas. Dá para agregar essas chamadas na camada Edge e reduzir o número de requisições do cliente.
export const runtime = 'edge'
export async function GET(request) {
// Faz várias requisições de API em paralelo
const [weather, news] = await Promise.all([
fetch('https://api.weather.com/...'),
fetch('https://api.news.com/...')
])
return Response.json({
weather: await weather.json(),
news: await news.json()
})
}
O usuário faz uma única requisição. O backend resolve tudo em paralelo e a latência total cai bastante.
Cenário 4: teste A/B
Na camada Edge, você decide qual versão do conteúdo devolver sem mexer na aplicação principal.
export const runtime = 'edge'
export async function GET(request) {
const userId = request.headers.get('x-user-id')
// Lógica simples de divisão A/B
const variant = parseInt(userId) % 2 === 0 ? 'A' : 'B'
const content = variant === 'A' ? getContentA() : getContentB()
return Response.json({ variant, content })
}
Limitações das Edge Functions
Se Edge é tão bom, por que não migrar tudo? Porque há várias limitações:
Limitação 1: não dá para usar APIs exclusivas do Node.js
fs, path e child_process não funcionam. Se o código depende disso, migrar para Edge vai quebrar.
Limitação 2: conexão com banco de dados
Conectores tradicionais de banco, como pg e mysql2, não funcionam porque dependem do módulo net do Node.js. Use soluções HTTP-based, como:
- Prisma Data Proxy
- PlanetScale (MySQL)
- Supabase (PostgreSQL)
- Redis com suporte a HTTP API
Limitação 3: memória e tempo de execução
Edge Functions normalmente têm limite de memória, como 128MB, e limite de execução, como 30 segundos. Processamento pesado ou grandes volumes de dados não combinam com esse ambiente.
Minha sugestão: use em conjunto
Não precisa escolher um lado só. Minha abordagem é:
- Camada de borda (Edge): autenticação, decisão por geolocalização e proxy simples.
- Camada central (Node.js): lógica de negócio complexa, operações no banco e processamento de arquivos.
A camada Edge segura requisições inválidas e casos simples. O que for complexo segue para o servidor central. Assim você reduz latência sem ficar preso às limitações da borda.
Teste real de desempenho
Segundo um benchmark publicado no Medium:
- Vercel Edge Functions: latência média de 48,3ms
- Cloudflare Workers (customizado): latência média de 36,37ms
- API tradicional em Node.js (região única): latência média de 200-500ms
Edge realmente é rápido, mas o resultado depende da distribuição dos seus usuários. Se todos estão no mesmo país, um servidor tradicional bem localizado pode ser mais rápido.
Caso prático combinado: otimização da API de lista de posts do blog
Até aqui falamos das três técnicas separadamente. Agora vamos juntar tudo em um caso real. Vamos usar a API de lista do blog do começo, aquela lenta o suficiente para gerar reclamação no teste de usuário.
Problemas antes da otimização
Veja o código original:
// app/api/posts/route.js
export async function GET() {
// Problema 1: consulta o banco a cada requisição, sem cache
const posts = await db.post.findMany({
take: 100,
include: {
author: true, // Problema 2: query N+1
tags: true
}
})
// Problema 3: retorna o conteúdo completo dos posts, com muito dado
return Response.json(posts)
}
Dados de desempenho:
- Tempo de resposta: 2800ms
- Tamanho do JSON: 2,3MB
- Experiência do usuário: tela branca por 3 segundos
Passo 1: otimizar a consulta ao banco
Primeiro, resolva o problema de N+1 queries e retorne apenas os campos necessários:
export async function GET() {
const posts = await db.post.findMany({
take: 100,
select: {
id: true,
title: true,
summary: true, // só o resumo, não o texto completo
createdAt: true,
author: {
select: { name: true, avatar: true }
}
}
})
return Response.json(posts)
}
Resultado: o tempo de resposta caiu para 800ms, e o JSON saiu de 2,3MB para 180KB.
Passo 2: adicionar cache
A lista de posts não muda a todo instante, então pode ficar em cache por 5 minutos:
export async function GET() {
const posts = await db.post.findMany({
// ... igual ao exemplo acima
}, {
next: {
revalidate: 300, // cache por 5 minutos
tags: ['posts']
}
})
return Response.json(posts)
}
Combine isso com limpeza de cache quando um post for publicado:
// app/api/posts/publish/route.js
import { revalidateTag } from 'next/cache'
export async function POST(request) {
const newPost = await request.json()
await db.post.create({ data: newPost })
// Limpa o cache da lista de posts
revalidateTag('posts')
return Response.json({ success: true })
}
Resultado: com cache hit, a resposta caiu para 50ms, e a carga do servidor baixou 90%.
Passo 3: trocar para resposta em streaming
Mesmo com tudo mais rápido, o primeiro acesso, quando o cache ainda não foi preenchido, ainda espera 800ms. Vamos usar streaming:
export async function GET() {
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
const batchSize = 20
for (let page = 0; page < 5; page++) {
const posts = await db.post.findMany({
skip: page * batchSize,
take: batchSize,
select: { /* igual ao exemplo acima */ }
})
const chunk = JSON.stringify(posts) + '\n'
controller.enqueue(encoder.encode(chunk))
}
controller.close()
}
})
return new Response(stream, {
headers: {
'Content-Type': 'application/x-ndjson', // Newline Delimited JSON
'Cache-Control': 's-maxage=300, stale-while-revalidate=600'
}
})
}
Resultado: o primeiro lote volta em 300ms. O usuário já consegue navegar, e o tempo total de 800ms praticamente desaparece da percepção.
Passo 4: autenticação na camada Edge (opcional)
Se a API precisa de autenticação, você pode fazer uma validação inicial na camada Edge:
// app/api/posts/route.js (camada de autenticação Edge)
export const runtime = 'edge'
export async function GET(request) {
const token = request.headers.get('authorization')
if (!token) {
return new Response('Não autorizado', { status: 401 })
}
// Depois da validação, encaminha para a API real, em Node.js Runtime
return fetch(`${process.env.API_BASE_URL}/posts/internal`, {
headers: { authorization: token }
})
}
Requisições inválidas são bloqueadas na borda e não chegam ao servidor central.
Comparação do resultado
| Métrica | Antes | Depois | Ganho |
|---|---|---|---|
| Tempo de resposta no primeiro acesso | 2800ms | 300ms (primeiro lote) | 89% ↓ |
| Tempo de resposta com cache hit | - | 50ms | 98% ↓ |
| Tamanho do JSON | 2,3MB | 180KB | 92% ↓ |
| Tempo até interação | 2800ms | 300ms | 89% ↓ |
| Carga do servidor | 100% | 10% | 90% ↓ |
O usuário nunca mais reclamou que parecia “um site de outra década”.
Monitoramento de desempenho e otimização contínua
Otimizar não é o fim. Você precisa monitorar continuamente para saber se o ganho continua real.
Métricas principais
Eu acompanho estes indicadores:
-
Distribuição do tempo de resposta (P50, P95, P99)
- P50, ou mediana: a experiência de metade dos usuários.
- P95: a experiência de 95% dos usuários.
- P99: a experiência do 1% mais lento, que costuma revelar casos anormais.
-
Taxa de acerto do cache
- Cache hit abaixo de 70% indica problema na estratégia de cache.
- Cache hit acima de 95% pode indicar tempo de cache longo demais e dados pouco frescos.
-
Taxa de erro
- Depois da otimização, a taxa de erro não pode subir.
- Streaming pode falhar no meio do caminho, então exige atenção extra.
-
Distribuição geográfica
- Diferença de latência entre regiões.
- Ajuda a decidir se Edge Functions são necessárias.
Ferramentas de monitoramento
Vercel Analytics: se você faz deploy na Vercel, o monitoramento de desempenho já vem integrado. Dá para ver a distribuição de tempo de resposta de cada API.
Next.js Instrumentation API (recurso novo em 2026): permite inserir pontos de monitoramento no código.
// instrumentation.js
export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
require('./monitoring')
}
}
// monitoring.js
export function onRequestEnd(info) {
console.log(`API ${info.url} levou ${info.duration}ms`)
// Envia para a plataforma de monitoramento
sendToMonitoring({
url: info.url,
duration: info.duration,
status: info.status
})
}
Logs customizados: simples, direto e ainda útil.
export async function GET() {
const start = Date.now()
const data = await fetchData()
const duration = Date.now() - start
console.log(`API /posts levou ${duration}ms`)
return Response.json(data)
}
Sugestões para otimização contínua
- Revise a estratégia de cache periodicamente: se o negócio muda, o cache também precisa mudar.
- Faça testes A/B: não sabe qual abordagem é melhor? Teste.
- Ajuste com dados reais: não decida no palpite; olhe as métricas antes.
Otimização de desempenho é um processo contínuo, não algo que você resolve uma vez e esquece.
Resumo
Falamos bastante. O essencial é isto:
Estratégia de cache: escolha conforme a natureza dos dados. Dados estáticos aceitam cache longo. Dados de usuário combinam com cache curto e stale-while-revalidate. Dados em tempo real não devem ser cacheados. E não esqueça de limpar o cache quando os dados mudarem.
Resposta em streaming: é uma ótima saída quando há muito dado ou cálculo lento. Faça o usuário ver conteúdo mais cedo, em vez de encarar uma tela branca. Com Virtual Scrolling no frontend, fica ainda melhor.
Edge Functions: fazem sentido para autenticação, decisão por geolocalização e proxy de API. Não espere que resolvam lógica de negócio complexa. O caminho mais saudável é combinar Edge com Node.js Runtime.
Otimização não acontece de uma vez só. Comece pelo endpoint mais lento, aplique essas três ideias, meça o resultado e ajuste. Vá passo a passo, sem tentar buscar perfeição de primeira.
A API de lista do meu blog saiu de 3 segundos para 300ms, e a melhora na experiência foi bem visível. Você também pode escolher um endpoint lento e começar hoje. Se tiver dúvida, deixe um comentário; vamos melhorar isso juntos.
FAQ
Quando o cache de uma API no Next.js expira?
Resposta em streaming serve para qualquer endpoint?
Quais são as limitações das Edge Functions?
Como escolher uma estratégia de cache?
Como validar o resultado depois da otimização?
18 min de leitura · Publicado em: 5 jan 2026 · Atualizado em: 14 jul 2026
Guia completo Next.js
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia completo de API Routes no Next.js: de Route Handlers às melhores práticas de tratamento de erros
Guia completo de API Routes no Next.js: aprenda a criar Route Handlers, processar requisições, tratar erros e projetar respostas para desenvolver APIs de backend com Next.js.
Parte 18 de 51
Próximo
Segurança e autenticação de APIs no Next.js: guia prático completo de JWT a rate limiting
Guia prático completo de segurança para APIs no Next.js: aprenda a criar aplicações confiáveis e prontas para produção com autenticação JWT, configuração de CORS, rate limiting, validação de entrada e prevenção contra vulnerabilidades recentes.
Parte 20 de 51



Comentários
Entre com GitHub para comentar