CodingNeed.

3 · Connect, test and build · 18 MIN

Validate a query parameter at the boundary

Treat URL values as untrusted strings before using them.

A query parameter is always text, even when it looks like a number. Number conversion alone accepts surprising values such as empty text. First define a permitted syntax, then check numeric bounds. This example validates limit before slicing a fixed list. Validation is not authorization: it only establishes that the value fits an input contract. Returning 400 for malformed input makes the failure actionable for the client and keeps invalid data out of later logic.

Read raw as text, then inspect Number(raw). Test a normal value and each invalid value in the URL. Use searchParams.get for q and validate its length. Apply filter first and slice second.

Before you start

Install Node.js 22.12+ (or a supported newer LTS). Create a folder named codingneed-node. Open a terminal in that folder. Save ONE lesson example as lesson.mjs and run node lesson.mjs. No npm dependencies are required. HTTP lessons keep running until you press Ctrl+C; stop the previous server before starting another.

File for this example: lesson.mjs

New words, explained

queryParameter
A named value following ? in a URL.
boundary
The point where outside data enters your application.
contract
The accepted input and promised output of an operation.

Follow the example step by step

  1. Read raw as text, then inspect Number(raw).
  2. Test a normal value and each invalid value in the URL.
  3. Use searchParams.get for q and validate its length.
  4. Apply filter first and slice second.

You are ready to move on when: Missing limit uses 3. Zero, decimals and text produce 400. Search filters before the result limit is applied.

Read the example

import {createServer} from 'node:http';
const topics = ['React', 'SQL', 'Node.js'];
createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');
  res.setHeader('Content-Type', 'application/json');
  if (req.method !== 'GET' || url.pathname !== '/topics') { res.writeHead(404); res.end('{}'); return; }
  const raw = url.searchParams.get('limit') ?? '3';
  const limit = Number(raw);
  if (!/^[1-9][0-9]*$/.test(raw) || !Number.isSafeInteger(limit) || limit > 3) {
    res.writeHead(400); res.end(JSON.stringify({error:'limit must be an integer from 1 to 3'})); return;
  }
  res.end(JSON.stringify(topics.slice(0, limit)));
}).listen(3000, '127.0.0.1', () => console.log('Open http://127.0.0.1:3000/topics?limit=2'));
Check the expected output
/topics?limit=2 returns ["React","SQL"]. limit=0, limit=abc and limit=2.5 each return 400.

Your challenge

Add an optional q search parameter that filters titles case-insensitively before applying limit. Limit q to 40 characters.

Solution cost: Discuss the operations in this small example; rendering and I/O costs depend on the host. time · Proportional to the example’s retained data. space

Common trap

Validation cannot be replaced by escaping text or by trusting the browser form.

Study the project implementation
import {createServer} from 'node:http';
const topics = ['React', 'SQL', 'Node.js'];
createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');
  res.setHeader('Content-Type', 'application/json');
  if (req.method !== 'GET' || url.pathname !== '/topics') { res.writeHead(404); res.end('{}'); return; }
  const raw = url.searchParams.get('limit') ?? '3';
  const limit = Number(raw);
  if (!/^[1-9][0-9]*$/.test(raw) || !Number.isSafeInteger(limit) || limit > 3) {
    res.writeHead(400); res.end(JSON.stringify({error:'limit must be an integer from 1 to 3'})); return;
  }
  res.end(JSON.stringify(topics.slice(0, limit)));
}).listen(3000, '127.0.0.1', () => console.log('Open http://127.0.0.1:3000/topics?limit=2'));

Further reading: Official documentation

Next lesson: Write repeatable tests for the logic

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.