Node.js: Code Outside the Browser / Built in Modules

URL & Query Strings

πŸ“¦ The url Module

url is a built-in Node.js module for parsing and working with URLs β€” pulling out the protocol, host, path, and query string instead of slicing up a raw string yourself.

import { URL } from 'node:url';

πŸ’‘ URL and URLSearchParams are also available as globals in Node.js β€” this import is only needed if you want to be explicit about where they come from.


πŸ”— Parsing a URL

const myURL = new URL('<https://codeveda.dev:8080/blog/post?id=42&sort=asc#comments>');
console.log(myURL);
  • new URL(string) turns a URL string into a URL object, with each part already broken out for you.

  • This is the modern, standard way to work with URLs in Node β€” it's the same URL class browsers use.


🏷️ Reading the Parts

console.log(myURL.protocol); // 'https:'
console.log(myURL.hostname); // 'codeveda.dev'
console.log(myURL.port);     // '8080'
console.log(myURL.pathname); // '/blog/post'
console.log(myURL.search);   // '?id=42&sort=asc'
console.log(myURL.hash);     // '#comments'
console.log(myURL.href);     // the full URL as a string
  • protocol includes the trailing colon ('https:', not 'https').

  • hostname is just the domain; host would include the port too ('codeveda.dev:8080').

  • search is the raw query string, including the ?.

  • href gives you back the complete URL β€” handy after you've edited one of the other properties.


πŸ“¦ Handling Form Data & Query Strings β€” The Modern Way

Both of these boil down to the same thing: reading key=value pairs. Node's current APIs handle both with the same tools β€” no manual string-splitting, and no need for the older querystring module.

  • URL / URLSearchParams β€” global in Node.js, no import needed

  • node:stream/consumers β€” reads a request stream into text without manually collecting chunks

  • node:http β€” using the node: prefix is the current recommended way to import core modules


πŸ”Ή Reading Form Data (POST Body)

πŸ“„ index.html:

<form action="/submit" method="POST">
  <input name="name" placeholder="Enter name">
  <button type="submit">Submit</button>
</form>

πŸ‘¨β€πŸ’» server.js:

import { createServer } from 'node:http';
import { text } from 'node:stream/consumers';

const server = createServer(async (req, res) => {
  if (req.method === 'POST' && req.url === '/submit') {
    const body = await text(req); // 'name=Varsha'
    console.log('Raw Body:', body);

    const params = new URLSearchParams(body);
    const name = params.get('name');

    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(`You submitted: ${name}`);
  } else {
    res.writeHead(404);
    res.end('Page not found');
  }
});

server.listen(3000, () => {
  console.log('Server listening at <http://localhost:3000>');
});

Output β€” submit the form, and the response is:

You submitted: Varsha
  • text(req), from node:stream/consumers, reads the whole request stream and gives you back a string β€” no req.on('data') / req.on('end') bookkeeping needed.

  • A form-urlencoded body (name=Varsha) is in the exact same format as a URL query string, so URLSearchParams parses it directly.

  • params.get('name') reads the value safely β€” it handles multiple fields, special characters, and missing values correctly, unlike manually splitting on =.


πŸ”Ή Reading Query Parameters

A mini server that reads query parameters from a URL like:

<http://localhost:3000/greet?name=Varsha>

πŸ“„ server.js:

import { createServer } from 'node:http';

const server = createServer((req, res) => {
  const myUrl = new URL(req.url, `http://${req.headers.host}`);

  if (myUrl.pathname === '/greet') {
    const name = myUrl.searchParams.get('name') || 'Guest';
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(`Hello, ${name}!`);
  } else {
    res.writeHead(404);
    res.end('Page not found');
  }
});

server.listen(3000, () => {
  console.log('Server running on <http://localhost:3000>');
});

Output β€” visiting http://localhost:3000/greet?name=Varsha in a browser returns:

Hello, Varsha!
  • req.url only holds the path and query string (/greet?name=Varsha), not a full URL β€” so new URL() needs a base, which is why req.headers.host is passed as the second argument.

  • myUrl.pathname isolates the route, separate from the query string, so you can branch on it.

  • myUrl.searchParams.get('name') reads the parameter β€” the same URLSearchParams API used for the form body above.

  • The || 'Guest' fallback covers a missing ?name= so the server doesn't respond with Hello, null!.


πŸ’‘ Why This Is the Current Approach

  • One API β€” URLSearchParams β€” handles key-value pairs whether they come from a form body or a URL's query string.

  • node:stream/consumers replaces manual chunk-collecting (req.on('data')/req.on('end')) with a single await text(req) call.

  • URL and URLSearchParams are globals in Node.js β€” no import required for them specifically.

  • The node: prefix on imports (node:http, node:stream/consumers) is the current recommended style for built-in modules, making it explicit they're core Node APIs and not npm packages.

  • No dependency on the older querystring module anywhere in either example.