The global URL and URLSearchParams classes parse and build URLs correctly, encoding reserved characters and normalizing the structure. Hand-built query strings are a frequent source of encoding bugs and, when the host comes from user input, server-side request forgery.
Before you start
You should be comfortable with strings and the fetch API. This article covers URL construction and a security check; it assumes basic HTTP knowledge.
Step-by-step walkthrough
Step 1: Construct with URL, not concatenation
new URL('https://api.example.com/search') gives you a mutable object with searchParams. Setting a parameter encodes spaces and & correctly, so a b&c becomes a+b%26c without you writing an encoder. Concatenating strings skips that encoding and breaks on special characters.
Step 2: Read and manipulate parameters safely
url.searchParams.set('page', '2') replaces or adds a parameter, get returns the first value, and getAll returns all values for repeated keys. Iterating searchParams decodes values, so you never deal with percent-encoding by hand.
Step 3: Validate the host for outbound requests
When a URL is built from user input, parse it and check url.protocol and url.hostname against an allowlist before fetching. An unvalidated host enables SSRF, letting an attacker make the server call internal services. Reject everything not explicitly allowed.
Worked scenario
Parameters are set through URLSearchParams, which handles encoding.
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'a b&c');
url.searchParams.set('page', '2');
console.log(url.toString()); // https://api.example.com/search?q=a+b%26c&page=2
const host = new URL('https://evil.example').hostname;
console.log(host === 'api.example.com'); // falseWalk through the example
The space becomes + and the & inside the value becomes %26, so the query is unambiguous. The second part shows a parsed host that fails an allowlist check, which is the guard to apply before any fetch that uses a user-supplied URL. Parsing first then validating the components is the safe order.
Common mistake
Building query strings with ?a= + value, which double-encodes or leaves reserved characters raw. Another is passing a user-provided URL straight to fetch, which allows requests to internal metadata endpoints.
Verify the behavior
Assert that a value containing & and spaces round-trips through searchParams. Parse a URL with repeated keys and confirm getAll returns each value. Test the host allowlist with a valid host and a look-alike that must be rejected.
Interview exercise
Why is new URL safer than string concatenation for user input?
Answer and reasoning
new URL parses the input into components, so you can inspect the protocol and hostname separately and apply an allowlist, and it separates the host from the path so a user cannot smuggle a different authority. Concatenation treats the whole string as opaque, so an injected @ or // can redirect the request. Parsing turns a string into structured, checkable parts.
Continue learning
Compare request safety in Node file and path boundaries and Node body validation. Read the WHATWG URL API documentation and try the Node.js interview questions.