Routing by Hand

Routing is the logic that decides which response to send based on the request's URL path and HTTP method — and before frameworks like Express, you write it by hand with conditionals on req.url and req.method.

Learn Routing by Hand in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

By the end of this lesson you'll route requests with plain if and switch statements, parse paths and query strings safely with the URL class, and build a small method-aware route table that returns JSON with proper status codes like 404 and 405.

What You'll Learn in This Lesson

1️⃣ Routing with if on req.url and req.method

A Node server's request handler runs for every incoming request, no matter the URL. Routing is simply the code inside that handler that inspects the request and decides what to send back. The two properties you check are req.url (the path, like /about ) and req.method (the verb, like GET or POST ).

The pattern is: check a condition, respond, and return so no other branch runs. The crucial last branch is the 404 fallback — without it, unmatched requests hang forever because you never called res.end() . Read every line below, then run it.

2️⃣ Parsing the URL and Query Strings

There's a trap in Section 1: req.url includes the query string . A request to /greet?name=Sam has a req.url of "/greet?name=Sam" — so a plain req.url === '/greet' check silently fails. The fix is the built-in URL class.

Build a URL with (the second argument is a required base, since req.url is relative). Then url.pathname gives you the path with no query, and reads query values safely.

3️⃣ Method-Aware Routing and a Route Table

Real APIs serve the same path differently depending on the method: GET /users lists users, while POST /users creates one. So you branch first on the path, then on req.method inside it. Status codes matter here: send 200 for a successful read, 201 Created after a POST, 404 when the path doesn't exist, and 405 Method Not Allowed when the path exists but the verb is wrong.

To return JSON, set and pass your object through JSON.stringify(...) . A small sendJson helper keeps every branch tidy and consistent.

Your turn. The handler below works once you fill in the blanks marked ___ . Follow the 👉 hints, run it with node server.js , and compare with the expected output.

No blanks this time — just a brief and an outline. Build the route table yourself, run it, and check your output against the example in the comments. This is exactly the kind of hands-on routing that makes Express click later.

📋 Quick Reference — Manual Routing

Practice quiz

What two request properties do you inspect to route by hand?

  • req.body and req.headers
  • req.url and req.method
  • req.host and req.port
  • req.query and req.cookies

Answer: req.url and req.method. req.url gives the path and req.method gives the HTTP verb; together they decide the response.

Why must a manual router end with a 404 fallback?

  • To log requests
  • To set headers
  • Otherwise unmatched requests hang because res.end is never called
  • To enable HTTPS

Answer: Otherwise unmatched requests hang because res.end is never called. Without a catch-all that ends the response, an unmatched request never finishes and hangs.

Why is comparing req.url directly to '/greet' fragile?

  • req.url is always uppercase
  • req.url includes the query string, e.g. '/greet?name=Sam'
  • req.url is a number
  • req.url is undefined for GET

Answer: req.url includes the query string, e.g. '/greet?name=Sam'. req.url contains path plus query string, so an exact match fails when a query is present.

How do you parse the path out of req.url safely?

  • ?

Building a URL with a base lets you read url.pathname without the query string.

How do you read a query parameter like ?name=Sam?

  • url.searchParams.get('name')
  • req.params.name
  • url.query.name
  • req.body.name

Answer: url.searchParams.get('name'). url.searchParams.get('name') reads query values from the parsed URL.

What status code means the path exists but the HTTP verb is not allowed?

  • 404 Not Found
  • 405 Method Not Allowed
  • 200 OK
  • 500 Server Error

Answer: 405 Method Not Allowed. 405 Method Not Allowed signals the path matched but the method is unsupported; pair it with an Allow header.

When returning JSON manually, what must you do to the object?

  • Pass it directly to res.end
  • Set status 201 only
  • Pass it through JSON.stringify and set Content-Type application/json
  • Encrypt it

Answer: Pass it through JSON.stringify and set Content-Type application/json. res.end sends strings, so stringify the object and set the JSON Content-Type.

Why might a route for '/about' fail to match '/about/'?

  • Trailing slashes are reserved
  • /about/ is a query string
  • String comparison is exact, so the two differ
  • Node lowercases paths

Answer: String comparison is exact, so the two differ. '/about' and '/about/' are different strings, so the trailing slash slips past an exact check.

What does Express's app.get('/about', ...) do under the hood?

  • The same method-plus-path check you write by hand
  • Opens a database connection
  • Caches the response
  • Encrypts the route

Answer: The same method-plus-path check you write by hand. Frameworks are sugar over the same req.method and req.url checks plus a 404 fallback.

Why should you return after calling res.end() in a branch?

  • To restart the server
  • To free memory
  • To prevent a later branch writing to an already-finished response
  • To log the request

Answer: To prevent a later branch writing to an already-finished response. Returning stops later code from trying to respond again on a finished response.