DevQuestPROEstudos de Código
DevDocs & W3Schools EncyclopediaTry It Yourself Integrado

Enciclopédia de Métodos & Estruturas

Consulte métodos de JavaScript (Arrays, Strings, Objetos), estruturas de dados Pilha (Stack LIFO) & Fila (FIFO), SQL e CSS com execução de código em tempo real e complexidade Big-O.

Stack Playground InterativoLIFO: Last-In, First-Out

Experimente as operações de empilhar (push), desempilhar (pop) e consultar topo (peek) em tempo real!

Tamanho:3
Vazia?:false
Chamada 1 (main)
[0]
Chamada 2 (fetchUser)
[1]
Chamada 3 (validate)
[2]TOPO
Base da Pilha (Memória Heap)
Console da Pilha● Ativo
❯ Pilha inicializada com 3 frames.

Estruturas de Dados: Pilha (Stack - LIFO)

Stack / Estruturas
7 métodos

stack.push(elemento)

O(1)Mutável
stack.push(elemento)

Insere um novo elemento no topo da pilha. A operação possui tempo constante O(1).

class Stack {
  constructor() { this.items = []; }
  push(element) { this.items.push(element); }
  peek() { return this.items[this.items.length - 1]; }
}

const pilha = new Stack();
pilha.push("Prato 1");
pilha.push("Prato 2");
pilha.push("Prato 3");
console.log("Topo após pushes:", pilha.peek());
👉 Topo após pushes: Prato 3

stack.pop()

O(1)Mutável
const removido = stack.pop()

Remove e retorna o elemento que está no topo da pilha (o último que entrou). Retorna null se vazia.

class Stack {
  constructor() { this.items = ["A", "B", "C"]; }
  pop() {
    if (this.isEmpty()) return null;
    return this.items.pop();
  }
  isEmpty() { return this.items.length === 0; }
}

const pilha = new Stack();
const topoRemovido = pilha.pop();
console.log("Elemento desempilhado:", topoRemovido);
console.log("Novo topo:", pilha.items[pilha.items.length - 1]);
👉 Elemento desempilhado: C | Novo topo: B

stack.peek() / top()

O(1)Imutável
const topo = stack.peek()

Inspeciona o elemento atualmente no topo da pilha SEM removê-lo. Essencial para verificar precedência e balanceamento.

class Stack {
  constructor() { this.items = [10, 20, 30]; }
  peek() { return this.items[this.items.length - 1]; }
}

const s = new Stack();
console.log("Topo atual:", s.peek());
console.log("Tamanho permanece:", s.items.length);
👉 Topo atual: 30 | Tamanho permanece: 3

stack.isEmpty() & stack.size()

O(1)Imutável
stack.isEmpty(): boolean | stack.size(): number

Verifica se a pilha não contém elementos e retorna o número total de itens armazenados.

class Stack {
  constructor() { this.items = []; }
  isEmpty() { return this.items.length === 0; }
  size() { return this.items.length; }
}

const s = new Stack();
console.log("Está vazia?", s.isEmpty());
s.items.push("task-1");
console.log("Tamanho:", s.size());
console.log("Está vazia?", s.isEmpty());
👉 Está vazia? true -> Tamanho: 1 -> Está vazia? false

stack.clear()

O(1)Mutável
stack.clear()

Remove todos os elementos da pilha, redefinindo o ponteiro de topo ou limpando a lista.

class Stack {
  constructor() { this.items = [1, 2, 3, 4]; }
  clear() { this.items = []; }
  size() { return this.items.length; }
}

const s = new Stack();
s.clear();
console.log("Tamanho após clear:", s.size());
👉 Tamanho após clear: 0

Implementação Completa de Pilha (Classe ES6/TS)

O(1) para operações básicasMutável
class Stack<T> { ... }

Implementação robusta orientada a objetos com métodos e operações de pilha.

class Stack {
  constructor() { this._items = []; }
  push(element) { this._items.push(element); }
  pop() { return this._items.pop(); }
  peek() { return this._items[this._items.length - 1]; }
  isEmpty() { return this._items.length === 0; }
  size() { return this._items.length; }
  print() { return this._items.slice().reverse().join(" -> "); }
}

const p = new Stack();
p.push("Base");
p.push("Meio");
p.push("Topo");
console.log("Visualização (Topo à Base):", p.print());
👉 Visualização: Topo -> Meio -> Base

Desafio Real de Entrevista: Valid Parentheses

O(n) tempo, O(n) espaçoImutável
isValidParentheses(str: string): boolean

Algoritmo clássico de FAANG usando Pilha para verificar se parênteses, colchetes e chaves estão balanceados.

function isValidParentheses(s) {
  const stack = [];
  const map = { ')': '(', '}': '{', ']': '[' };
  for (const char of s) {
    if (['(', '{', '['].includes(char)) {
      stack.push(char);
    } else if (map[char]) {
      if (stack.pop() !== map[char]) return false;
    }
  }
  return stack.length === 0;
}

console.log("'{[()]}':", isValidParentheses("{[()]}"));
console.log("'{[(])}':", isValidParentheses("{[(])}"));
👉 '{[()]}': true | '{[(])}': false

Estruturas de Dados: Fila (Queue - FIFO)

Queue / Estruturas
2 métodos

queue.enqueue(elemento)

O(1)Mutável
queue.enqueue(elemento)

Insere um elemento no final da fila (fim da fila).

class Queue {
  constructor() { this.items = []; }
  enqueue(item) { this.items.push(item); }
  front() { return this.items[0]; }
}

const fila = new Queue();
fila.enqueue("Primeiro Cliente");
fila.enqueue("Segundo Cliente");
console.log("Primeiro da fila:", fila.front());
👉 Primeiro da fila: Primeiro Cliente

queue.dequeue()

O(1) com lista encadeada ou O(n) com array shiftMutável
const item = queue.dequeue()

Remove e retorna o primeiro elemento que entrou na fila (o mais antigo).

class Queue {
  constructor() { this.items = ["Email 1", "Email 2", "Email 3"]; }
  dequeue() { return this.items.shift(); }
  front() { return this.items[0]; }
}

const filaEnvio = new Queue();
const processado = filaEnvio.dequeue();
console.log("Processado:", processado);
console.log("Próximo da fila:", filaEnvio.front());
👉 Processado: Email 1 | Próximo da fila: Email 2

JavaScript: Métodos de Array

JavaScript
14 métodos

.map()

O(n)Imutável
const novoArray = array.map((item, index, arr) => novoValor)

Cria um novo array transformando cada elemento através de uma função callback. Não altera o array original.

const precos = [100, 250, 400];
const comDesconto = precos.map(p => p * 0.9);
console.log("Com 10% desconto:", comDesconto);
console.log("Original intacto:", precos);
👉 [90, 225, 360] (Imutável)

.filter()

O(n)Imutável
const filtrados = array.filter((item, index, arr) => condicaoBooleana)

Retorna um novo array contendo apenas os elementos que retornam true na condição testada.

const devs = [
  { nome: "Ana", nivel: "senior" },
  { nome: "Carlos", nivel: "junior" },
  { nome: "Julia", nivel: "senior" }
];
const seniors = devs.filter(d => d.nivel === "senior");
console.log("Apenas seniores:", seniors.map(s => s.nome));
👉 ['Ana', 'Julia'] (Imutável)

.reduce()

O(n)Imutável
const acumulado = array.reduce((acc, curr, index, arr) => novoAcc, valorInicial)

Executa uma função redutora em cada elemento, resultando em um único valor final (soma, objeto acumulado, etc).

const despesas = [45.5, 120.0, 32.9];
const total = despesas.reduce((acc, val) => acc + val, 0);
console.log("Total gasto:", total.toFixed(2));
👉 198.40 (Imutável)

.find() & .findIndex()

O(n)Imutável
array.find(fn) | array.findIndex(fn)

.find() retorna o PRIMEIRO elemento que satisfaz a condição (ou undefined). .findIndex() retorna o índice ou -1.

const produtos = [
  { id: 101, nome: "Teclado" },
  { id: 102, nome: "Mouse" }
];
const item = produtos.find(p => p.id === 102);
const idx = produtos.findIndex(p => p.id === 102);
console.log("Encontrado:", item.nome, "no índice:", idx);
👉 Mouse no índice 1

.some() & .every()

O(n)Imutável
array.some(condicao) | array.every(condicao)

.some() verifica se AO MENOS UM elemento cumpre a regra. .every() verifica se TODOS cumprem a regra.

const notas = [8, 9, 5, 10];
const algumReprovado = notas.some(n => n < 6);
const todosAprovados = notas.every(n => n >= 6);
console.log("Algum < 6?", algumReprovado);
console.log("Todos >= 6?", todosAprovados);
👉 Algum < 6? true | Todos >= 6? false

.slice() (Imutável)

O(k)Imutável
array.slice(inicio, fim)

Retorna uma cópia rasa (shallow copy) de uma fatia do array sem modificar o original. Ideal para React!

const frutas = ["Maçã", "Banana", "Uva", "Manga"];
const fatia = frutas.slice(1, 3);
console.log("Fatia:", fatia);
console.log("Original:", frutas);
👉 Fatia: ['Banana', 'Uva'] | Original intacto

.splice() (Mutável)

O(n)Mutável
array.splice(inicio, qtdExcluir, ...novosItens)

Altera o array original removendo ou inserindo novos elementos no local especificado.

const cores = ["Vermelho", "Verde", "Azul"];
const removidos = cores.splice(1, 1, "Amarelo", "Roxo");
console.log("Removido:", removidos);
console.log("Array modificado:", cores);
👉 Removido: ['Verde'] | cores: ['Vermelho', 'Amarelo', 'Roxo', 'Azul']

.includes() & .indexOf()

O(n)Imutável
array.includes(valor, deIndice) | array.indexOf(valor)

includes verifica se o elemento exato existe (retorna boolean). indexOf retorna a posição ou -1.

const linguagens = ["Python", "JavaScript", "Rust"];
console.log("Tem Rust?", linguagens.includes("Rust"));
console.log("Posição de JavaScript:", linguagens.indexOf("JavaScript"));
👉 Tem Rust? true | Posição: 1

.sort() (Mutável)

O(n log n)Mutável
array.sort((a, b) => a - b)

Ordena os elementos no próprio array. ATENÇÃO: por padrão ordena como strings ('10' vem antes de '2'). Sempre use compareFunction para números.

const numeros = [40, 100, 1, 5, 25, 10];
numeros.sort((a, b) => a - b);
console.log("Ordenado crescente:", numeros);
👉 [1, 5, 10, 25, 40, 100]

.reverse() (Mutável) & .toReversed()

O(n)Mutável
array.reverse() | array.toReversed()

reverse() inverte a ordem dos itens modificando o array original. toReversed() (ES2023) cria uma cópia invertida.

const fila = ["A", "B", "C"];
fila.reverse();
console.log("Invertido:", fila);
👉 ['C', 'B', 'A']

.flat() & .flatMap()

O(n)Imutável
array.flat(profundidade) | array.flatMap(fn)

flat() achata arrays aninhados. flatMap() mapeia e achata o resultado em 1 nível.

const matriz = [1, [2, [3, 4]]];
console.log("Nível 1:", matriz.flat(1));
console.log("Total:", matriz.flat(Infinity));

const frases = ["olá mundo", "dev quest"];
console.log("Palavras:", frases.flatMap(f => f.split(" ")));
👉 Total: [1, 2, 3, 4] | Palavras: ['olá', 'mundo', 'dev', 'quest']

.push(), .pop(), .shift(), .unshift()

push/pop: O(1) | shift/unshift: O(n)Mutável
push / pop (no final) | shift / unshift (no início)

Manipulam as pontas do array. push/pop operam em O(1); shift/unshift exigem reindexação em O(n).

const lista = [10, 20];
lista.push(30); // [10, 20, 30]
lista.unshift(0); // [0, 10, 20, 30]
const ultimo = lista.pop(); // remove 30
const primeiro = lista.shift(); // remove 0
console.log("Resultado:", lista, "Removidos:", primeiro, ultimo);
👉 Resultado: [10, 20] | Removidos: 0, 30

.at() (Acesso com Índices Negativos)

O(1)Imutável
array.at(indice)

Permite acessar itens pelo índice, aceitando números negativos para contar de trás para frente.

const langs = ["TypeScript", "Go", "Rust", "C++"];
console.log("Último:", langs.at(-1));
console.log("Penúltimo:", langs.at(-2));
👉 Último: C++ | Penúltimo: Rust

Array.from() & Array.isArray()

O(n)Imutável
Array.from(iteravel, mapFn) | Array.isArray(obj)

Array.from converte iteráveis (Set, Map, NodeList, String) em arrays. Array.isArray valida se uma variável é array.

const conjunto = new Set([1, 2, 3, 2, 1]);
const arr = Array.from(conjunto, x => x * 10);
console.log("De Set para Array:", arr);
console.log("É array?", Array.isArray(arr));
👉 [10, 20, 30] | É array? true

JavaScript: Métodos de String

JavaScript
7 métodos

.split()

O(n)Imutável
string.split(separador, limite)

Divide uma string em um array de substrings com base em um separador especificado.

const csv = "Henrique,25,Desenvolvedor";
const dados = csv.split(",");
console.log("Array resultante:", dados);
console.log("Primeiro item:", dados[0]);
👉 ['Henrique', '25', 'Desenvolvedor']

.slice() & .substring()

O(k)Imutável
string.slice(inicio, fim)

Extrai uma seção da string e a retorna como uma nova string. Aceita índices negativos.

const slug = "artigo-como-aprender-react";
const prefixo = slug.slice(0, 6);
const final = slug.slice(-5);
console.log("Início:", prefixo, "| Fim:", final);
👉 Início: artigo | Fim: react

.replace() & .replaceAll()

O(n)Imutável
string.replace(alvo, substituto) | string.replaceAll(alvo, substituto)

Substitui ocorrências de um padrão (string ou RegExp) por outro texto.

const texto = "gato preto, gato branco";
console.log("replace:", texto.replace("gato", "cachorro"));
console.log("replaceAll:", texto.replaceAll("gato", "cachorro"));
👉 replace: cachorro preto... | replaceAll: cachorro preto, cachorro branco

.includes(), .startsWith(), .endsWith()

O(n)Imutável
str.includes(busca) | str.startsWith(busca) | str.endsWith(busca)

Testes rápidos de presença e posicionamento de caracteres sem necessidade de Regex.

const email = "contato@devquest.com.br";
console.log("Contém devquest?", email.includes("devquest"));
console.log("Termina com .br?", email.endsWith(".br"));
console.log("Começa com contato?", email.startsWith("contato"));
👉 true, true, true

.trim(), .trimStart(), .trimEnd()

O(n)Imutável
string.trim()

Remove espaços em branco no início e no final de strings (sanitização de inputs).

const input = "   usuario@email.com   \n";
console.log("Original:", JSON.stringify(input));
console.log("Trimmed:", JSON.stringify(input.trim()));
👉 'usuario@email.com'

.toLowerCase() & .toUpperCase()

O(n)Imutável
string.toLowerCase() | string.toUpperCase()

Converte todos os caracteres da string para minúsculas ou maiúsculas.

const query = "TypeScript Pro";
console.log("Minusculo:", query.toLowerCase());
console.log("Maiusculo:", query.toUpperCase());
👉 'typescript pro' | 'TYPESCRIPT PRO'

.padStart() & .padEnd()

O(n)Imutável
string.padStart(comprimentoAlvo, preenchimento)

Preenche a string com outro caractere até atingir o tamanho desejado (ex: formatação de números e cartões).

const id = "42";
const codigo = id.padStart(6, "0");
console.log("Código formatado:", codigo);

const finalCartao = "1234".padStart(16, "*");
console.log("Cartão mascarado:", finalCartao);
👉 '000042' | '************1234'

JavaScript: Métodos de Objeto

JavaScript
5 métodos

Object.keys(), Object.values(), Object.entries()

O(n)Imutável
Object.keys(obj) | Object.values(obj) | Object.entries(obj)

Extraem arrays com os nomes das propriedades, seus valores correspondentes ou pares [chave, valor].

const stats = { vidas: 3, xp: 450, nivel: 5 };
console.log("Chaves:", Object.keys(stats));
console.log("Valores:", Object.values(stats));
console.log("Entradas:", Object.entries(stats));
👉 Chaves: ['vidas', 'xp', 'nivel'] | Valores: [3, 450, 5]

Object.assign() & Spread Operator

O(n)Imutável
Object.assign(destino, ...fontes) | { ...obj1, ...obj2 }

Copia todas as propriedades enumeráveis de um ou mais objetos de origem para um objeto de destino.

const usuario = { id: 1, nome: "Lucas" };
const permissoes = { role: "admin", ativo: true };
const atualizado = Object.assign({}, usuario, permissoes);
console.log("Mesclado:", atualizado);
👉 { id: 1, nome: 'Lucas', role: 'admin', ativo: true }

Object.freeze() & Object.seal()

O(n)Mutável
Object.freeze(obj) | Object.seal(obj)

freeze torna o objeto 100% imutável (não adiciona, nem remove, nem altera). seal impede adicionar/remover propriedades.

const config = Object.freeze({ api: "https://api.devquest.com" });
// Tentativa de alteração silenciosamente ignorada
config.api = "http://hack.com";
console.log("Config protegida:", config.api);
👉 https://api.devquest.com (Inalterado)

Object.fromEntries()

O(n)Imutável
Object.fromEntries(paresChaveValor)

Converte uma lista de pares chave-valor (como um Map ou retorno de Object.entries) de volta em um objeto literal.

const precosEntries = [["mouse", 120], ["teclado", 350]];
const obj = Object.fromEntries(precosEntries);
console.log("Objeto gerado:", obj);
👉 { mouse: 120, teclado: 350 }

Object.hasOwn() (ES2022)

O(1)Imutável
Object.hasOwn(obj, 'propriedade')

Substituto seguro e moderno para obj.hasOwnProperty(), que funciona mesmo com objetos criados via Object.create(null).

const produto = { id: 99, emEstoque: true };
console.log("Tem estoque?", Object.hasOwn(produto, "emEstoque"));
console.log("Tem desconto?", Object.hasOwn(produto, "desconto"));
👉 true | false

JavaScript: Promises & Async/Await

Async
3 métodos

Promise.all()

O(n)Imutável
Promise.all([p1, p2, p3])

Executa múltiplas promises em paralelo. Falha IMEDIATAMENTE (fail-fast) se qualquer uma for rejeitada.

const buscarUsuarios = () => Promise.resolve(["Ana", "Bob"]);
const buscarConfig = () => Promise.resolve({ tema: "dark" });

Promise.all([buscarUsuarios(), buscarConfig()])
  .then(([usuarios, config]) => {
    console.log("Dados carregados juntos:", usuarios, config.tema);
  });
👉 Dados carregados juntos: ['Ana', 'Bob'] dark

Promise.allSettled()

O(n)Imutável
Promise.allSettled([p1, p2, p3])

Aguarda TODAS as promises terminarem, independentemente de terem sido resolvidas ou rejeitadas, retornando os status individuais.

const p1 = Promise.resolve("Sucesso");
const p2 = Promise.reject("Erro de rede");

Promise.allSettled([p1, p2]).then(resultados => {
  console.log("Resultados:", resultados.map(r => r.status));
});
👉 Resultados: ['fulfilled', 'rejected']

Promise.race() vs Promise.any()

O(1) após primeira resoluçãoImutável
Promise.race(iteravel) | Promise.any(iteravel)

race() resolve ou rejeita assim que a primeira terminar. any() espera a PRIMEIRA que tiver SUCESSO (ignora falhas até que todas falhem).

const rapida = new Promise(res => setTimeout(() => res("Servidor 1"), 50));
const lenta = new Promise(res => setTimeout(() => res("Servidor 2"), 200));

Promise.race([rapida, lenta]).then(vencedor => {
  console.log("Mais rápido:", vencedor);
});
👉 Mais rápido: Servidor 1

CSS: Flexbox & CSS Grid Moderno

CSS
4 métodos

justify-content (Eixo Principal)

Hardware Accelerated
justify-content: flex-start | center | flex-end | space-between | space-around | space-evenly;

Controla o alinhamento horizontal dos filhos (ou vertical se flex-direction for column).

.container {
  display: flex;
  justify-content: space-between;
}
👉 space-between coloca o primeiro e último item nas bordas.

align-items (Eixo Cruzado)

align-items: stretch | center | flex-start | flex-end | baseline;

Alinha os itens no eixo perpendicular. 'center' é a forma mais fácil de centralizar verticalmente.

.container {
  display: flex;
  height: 200px;
  align-items: center;
}
👉 Para centralização perfeita use: justify-content: center; align-items: center;

gap (Espaçamento Nativo)

gap: 16px; | gap: row-gap column-gap;

Cria espaçamento uniforme entre os itens filhos sem precisar de margins individuais.

.grid-menu {
  display: flex;
  gap: 12px;
}
👉 Suportado em 100% dos navegadores modernos.

CSS Grid: grid-template-columns com auto-fit

grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

Cria um grid 100% responsivo sem precisar de nenhuma media query.

.cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
}
👉 Os cards se reorganizam automaticamente conforme a largura da tela.

SQL: Comandos, Joins & Agregações

SQL
4 métodos

INNER JOIN & LEFT JOIN

SELECT ... FROM t1 INNER JOIN t2 ON t1.fk = t2.pk; | LEFT JOIN ...

INNER JOIN retorna apenas correspondências exatas. LEFT JOIN mantém todas as linhas da tabela esquerda mesmo sem par.

SELECT clientes.nome, pedidos.total
FROM clientes
LEFT JOIN pedidos ON clientes.id = pedidos.cliente_id;
👉 Retorna todos os clientes, com NULL nos totais se não tiverem pedidos.

GROUP BY com HAVING

SELECT categoria, COUNT(*), AVG(preco) FROM produtos GROUP BY categoria HAVING AVG(preco) > 50;

Agrupa registros e permite calcular métricas agregadas. HAVING filtra resultados calculados pelas funções de agregação.

SELECT categoria, COUNT(*) as qtd, AVG(preco) as media
FROM produtos
GROUP BY categoria
HAVING COUNT(*) >= 5;
👉 Filtra apenas categorias que possuem 5 ou mais produtos cadastrados.

ORDER BY & LIMIT / OFFSET

ORDER BY coluna ASC/DESC LIMIT 10 OFFSET 20;

Ordena os registros e aplica paginação determinística no banco de dados.

SELECT id, titulo, visualizacoes
FROM artigos
ORDER BY visualizacoes DESC
LIMIT 10 OFFSET 0;
👉 Página 1 dos 10 artigos mais visualizados.

CASE WHEN (Condicional SQL)

CASE WHEN condicao THEN valor1 ELSE valor2 END

Executa lógica condicional dentro da própria consulta SQL (equivalente ao if/else ou ternário).

SELECT nome, preco,
  CASE
    WHEN preco > 500 THEN 'Premium'
    WHEN preco > 100 THEN 'Intermediário'
    ELSE 'Econômico'
  END AS categoria_faixa
FROM produtos;
👉 Cria coluna dinâmica categorizada diretamente no banco de dados.

Git: Fluxo de Trabalho & Comandos

Git
3 métodos

git stash

git stash | git stash pop | git stash list

Guarda modificações não commitadas em uma pilha temporária para limpar a árvore de trabalho.

git stash save "ajustes pendentes"
git checkout main
git pull
git checkout feature
git stash pop
👉 Recupera os arquivos mantendo o histórico de commits intacto.

git cherry-pick

git cherry-pick <commit-hash>

Aplica as mudanças de um commit específico de outra ramificação para a branch atual.

git checkout production
git cherry-pick a1b2c3d
👉 Útil para aplicar um hotfix sem mesclar toda a branch de desenvolvimento.

git reset vs git revert

git reset --soft/--hard HEAD~1 | git revert <hash>

Reset reescreve o histórico local; revert cria um NOVO commit que desfaz as alterações com segurança pública.

git revert HEAD # Seguro para branches compartilhadas no GitHub
👉 Em branches públicas (ex: main), sempre use git revert para não quebrar os colegas.

HTTP: Códigos de Status da API

HTTP / REST
4 métodos

200 OK & 201 Created

200 OK (GET/PUT) | 201 Created (POST)

200: Sucesso geral; 201: Novo recurso criado com sucesso no banco de dados.

res.status(201).json({ id: novoUsuario.id, message: "Criado!" });
👉 Sempre retorne 201 ao cadastrar entidades novas.

400 Bad Request & 422 Unprocessable

400 (Payload inválido) | 422 (Erro de validação semântica)

Indica que o cliente enviou dados malformados ou faltando campos obrigatórios.

res.status(400).json({ error: "O campo 'email' é obrigatório." });
👉 Geralmente retornado por validações do Zod ou Joi.

401 Unauthorized vs 403 Forbidden

401 (Não autenticado) | 403 (Não autorizado/permissão insuficiente)

401: Token JWT ausente ou expirado; 403: Usuário autenticado mas sem permissão de acesso.

if (!session) return res.status(401);
if (user.role !== 'admin') return res.status(403);
👉 401 = Quem é você? / 403 = Você não tem permissão aqui.

429 Too Many Requests

429 Too Many Requests (Rate Limiting)

O cliente enviou muitas requisições em um determinado período de tempo.

headers: { 'Retry-After': '60' }
👉 Protege sua aplicação contra abusos e ataques de força bruta.