Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Routing: validate path parameters and return a stable error shape

Last updated: 5 Oct 20266 min read
tutorial
IntermediateBy AITrove Editorial

A route pattern can capture a path segment, but the captured bytes remain untrusted text. Parse a case ID under a strict rule before asking storage for a record. A malformed identifier should not accidentally become a different valid number through permissive parsing. The route then decides whether the caller can read that specific case, queries storage, and returns a predictable response. The client needs a stable error shape to show a useful message without guessing from arbitrary prose. An unauthorized response must not reveal private record details; choose 403 or a deliberately concealed 404 policy consistently. The example keeps parsing, permission, and lookup as separate checks.

Case study

A reviewer requests case 47 and receives the record. The path '/cases/47extra' is malformed and returns 400, not case 47. A valid but absent case 83 returns 404. A case outside the reviewer's assignment returns 403 in this model, without including the private title. The server's ordering is deliberate: a strict path parser prevents accidental coercion, then authorization decides whether a valid identifier may be read. A larger service should make the concealment policy explicit if it does not wish to reveal which IDs exist.

Working contract

javascript
const cases = new Map([[47, { title: "Pump inspection" }]]);
const assignedCaseIds = new Set([47, 83]);
function readCase(rawId) {
  if (!/^[1-9][0-9]*$/.test(rawId)) return { status: 400, code: "invalid_case_id" };
  const caseId = Number(rawId);
  if (!Number.isSafeInteger(caseId)) return { status: 400, code: "invalid_case_id" };
  if (!assignedCaseIds.has(caseId)) return { status: 403, code: "forbidden" };
  const record = cases.get(caseId);
  return record ? { status: 200, record } : { status: 404, code: "not_found" };
}
console.log(readCase("47").status);
console.log(readCase("47extra").code);
console.log(readCase("83").status);

Observed output

Output
200
invalid_case_id
404

Cost and tradeoffs

Parsing an ID string of length L takes O(L) time. The membership checks use expected O(1) time and O(N) storage for N case assignments and records. A database-backed route has additional query cost and must avoid fetching or serializing sensitive fields before authorization. A stable error object adds a few bytes but avoids brittle client text matching. Keep error detail safe for the recipient while retaining richer diagnostic context in protected server logs.

Common Mistakes

  • Do not use parseInt on a path and silently accept a trailing suffix.
  • Do not serialize a private case before checking permission.
  • Do not make clients infer error type from free-form prose.
  • Do not reveal a forbidden record title inside an error body.

Continue through the stack

HTTP requests: keep method, status, and body contracts separate; Form submission: validate on the server and return field errors; Sessions and CSRF: keep identity on the server.

Failure trace

The server parses /cases/47 as an integer but accepts /cases/47notes by reading only the initial digits. A second route returns the same 404 for an unknown case and a database outage, so the client offers a misleading 'create case' action during an incident. Match the whole route parameter, validate its shape, and distinguish missing resources from unavailable dependencies without exposing internals.

Verification

  • Request a valid ID, a malformed suffix, and an ID that has no record; compare status and body shape.
  • Disable the database and verify the route does not pretend the record is absent.
  • Request another user's valid case and confirm the disclosure policy does not expose its details.

Decision note

A path parameter selects a candidate resource; it does not grant access. Parse first, authenticate and authorize next, then read or mutate within the service's error contract.

web-tech
web-development
Storage details