Use URL Constructor ao invés de template literals para escrever suas URLs

Fala pessoal, hoje eu vim trazer uma dica rápida para vocês que ajudou a tornar meu código mais semântico.

Muitas vezes, trabalhando com backend ou frontend, precisamos fazer algumas requisições com alguns parâmetros, certo?

E eu costumava escrever a URL das minhas requisições dessas forma:

const url = `http://localhost:3000/endpoint?param1=${var1}&param2=${var2}&param3=${var3}`

Convenhamos que essa URL é difícil de ler e até de dar manutenção, sempre precisamos identificar o que é parâmetro, o que é variável e o que é apenas a sintaxe do código Javascript.

Para resolver esse problema de semântica, descobri o URL Constructor, que realiza a mesma função que o código acima, porém de uma forma mais eficiente e elegante.

Então, podemos reescrever o mesmo código desta forma:

const url = new URL('http://localhost:3000/endpoint')

url.searchParams.set('param1', var1)
url.searchParams.set('param2', var2)
url.searchParams.set('param3', var3)

O código fala por si só. Na primeira linha, criamos a URL base do endpoint e nas linhas seguintes adicionamos os search params necessários.

Pronto, agora a variável url contém os mesmos search params que antes, mas agora utilizando a classe URL, deixando o código mais simples e muito mais fácil de manter.

E você, já tinha utilizado a classe URL alguma vez? Quem sabe para alguma outra finalidade? Me conta aí.

Muito bom!

Este é um recurso que - minha impressão - poucas pessoas usam no dia-a-dia (infelizmente).

Outro ponto muito importante é que o searchParams já cuida de vários detalhes chatos pra vc. Por exemplo, se o valor do parâmetro tiver caracteres como & ou =, ele já trata corretamente:

const url = new URL('http://localhost:3000/endpoint');
url.searchParams.set('param1', 'a&b=c');

console.log(url.toString()); // http://localhost:3000/endpoint?param1=a%26b%3Dc

Repare que ao converter a URL para string, já é feito automaticamente o URL encoding dos caracteres & e = para %26 e %3D. Assim o valor é enviado corretamente (no caso, o parâmetro param1 com o valor a&b=c).

Se eu fizesse a concatenação de strings, a URL ficaria http://localhost:3000/endpoint?param1=a&b=c, ou seja, seriam enviados dois parâmetros (param1 com o valor a e b com o valor c). Para ficar correto, vc teria que fazer a substituição manualmente, mas pra que fazer isso se o searchParams já cuida disso pra vc?


Outro caso interessante que ele já trata é quando a URL tem anchor, pois este sempre deve ficar no final.

const url = new URL('http://localhost:3000/endpoint#anchor');
url.searchParams.set('param1', 'a');

console.log(url.toString()); // http://localhost:3000/endpoint?param1=a#anchor

console.log(url.searchParams); // URLSearchParams { 'param1' => 'a' }
console.log(url.hash); // #anchor

Ou seja, se vc sempre adicionar os parâmetros no final, ficaria http://localhost:3000/endpoint#anchor?param1=a. Mas está errado, pois neste caso, ela fica sem nenhum parâmetro, e o anchor é tudo que vem depois do #, veja:

// ERRADO: adicionar o parâmetro depois do anchor
const url = new URL('http://localhost:3000/endpoint#anchor?param1=a');

console.log(url.searchParams); // URLSearchParams {} (vazio)
console.log(url.hash); // #anchor?param1=a

Além disso, o searchParams permite fácil acesso aos parâmetros:

const url = new URL('http://localhost:3000/endpoint?param1=a&param2=b&param3=c');

// obter um parâmetro específico
console.log(url.searchParams.get('param1')); // a

// loop por todos
for (const [nome, valor] of url.searchParams) {
    console.log(`${nome} = ${valor}`);
}

Se pesquisar por aí, vc até vai encontrar "soluções" que envolvem regex ou concatenação de strings. E pode até "funcionar", até o momento em que vc se depara com estes casos especiais. O mais correto e garantido é usar o searchParams, que já cuida de todos esses casos.


Outra vantagem de se usar o objeto URL é que dá para separar facilmente os componentes da URL:

const url = new URL('http://user:senha@localhost:3000/endpoint/v1?param1=a&param2=b#anchor');

console.log(url.protocol); // http:
console.log(url.username); // user
console.log(url.password); // senha
console.log(url.host); // localhost:3000
console.log(url.hostname); // localhost
console.log(url.port); // 3000
console.log(url.pathname); // /endpoint/v1
console.log(url.search); // ?param1=a&param2=b
console.log(url.hash); // #anchor

Até daria pra fazer uma regex (a "solução" mais comum se vc pesquisar), mas é algo bem propenso a erros, e se torna tão complexo para tratar casos especiais que acaba não valendo a pena.

Por fim, outra vantagem é que o construtor dá erro caso a URL seja inválida (por exemplo, new URL('abc') lança um TypeError), ou seja, vc também já garante a validação - o que é mais trabalhoso se for verificar a string manualmente.


Para mais detalhes, consulte a documentação:

Já trabalhei em lugares que preferiram regex ao url, eu ja bati tanta boca por causa disso kkkk, sendo que é muito prático, semantico e simples, um ótimo conhecimento pra quem ainda não sabe ou não precisou implementar algo com urls no JS, uma mão na roda sem dúvidas

Eu faço da primeira forma. Não sei dizer muito bem o motivo, mas me parece mais natural, mais fluido do que a segunda opção. Mas foi bem útil a informação.