Your First Express Server: Routes, Parameters and Query Strings

MERN Stack Full Course: MongoDB, Express, React and Node.js · Node.js and Express · 4 min read · Updated

A request meets a route

  1. A request arrives with a method and a path.
  2. Express checks its routes from the top, looking for the first match.
  3. The path /jobs/:id matches, and the :id part is saved in req.params.
  4. Query strings such as ?status=applied are in req.query. The handler reads them and builds a reply.
  5. res.json sends the answer with a status code, and the request is finished.

In lesson 5 you wrote a REST server by hand and saw how much work it was. Express is a small, very widely used library that does that plumbing for you: it matches the method and path, parses the URL, and gives you simple tools to send the answer. The server you build from this lesson is the same one that runs the finished Job Tracker.

This course uses Express 5, the current major version. If you find older tutorials for Express 4, nearly everything still applies. The most useful difference is that Express 5 automatically passes errors from async functions to your error handler, which removes a lot of try/catch code (you will see it in lesson 12).

The smallest server

// Lesson 9: the first Express server.
import 'dotenv/config';
import express from 'express';

const app = express();
const port = Number(process.env.PORT) || 5000;

// A route = an HTTP method + a path + a function that answers.
app.get('/api/health', (req, res) => {
  res.json({ ok: true, time: new Date().toISOString() });
});

// Route parameters and query strings
app.get('/api/hello/:name', (req, res) => {
  const shout = req.query.shout === 'true';
  const text = `Hello, ${req.params.name}!`;
  res.json({ message: shout ? text.toUpperCase() : text });
});

app.listen(port, () => console.log(`API listening on http://localhost:${port}`));

Read it from the top. express() creates the app. app.get(path, handler) registers a route: when a GET request for that path arrives, Express calls the handler with two objects. req is the request: it holds the method, path, headers, parameters and body. res is the response: you call res.json(...) to send JSON, res.status(201) to set a status code, res.sendFile(...) to send a file. app.listen(port, callback) starts waiting for connections.

Route parameters and query strings

Two ways to send small pieces of information in the address itself are used constantly.

  • Route parameters are part of the path, marked with a colon: /api/hello/:name. A request for /api/hello/Asha gives req.params.name === 'Asha'. Use them to say *which* thing: /api/jobs/64f....
  • Query strings come after a question mark: /api/hello/Asha?shout=true. Express puts them in req.query, so req.query.shout === 'true'. Use them for *options*: search text, filters, sorting and page numbers. Remember that all values arrive as text.

The rule of thumb: the path identifies a resource, and the query string adjusts how you view it. That is exactly how the Job Tracker's list route will work: GET /api/jobs?status=applied&page=2.

Order matters

Express checks routes in the order you wrote them and stops at the first match. If you put /api/jobs/:id before /api/jobs/stats, a request for stats is matched as an id. Put the specific routes first. You will meet this exact bug in lesson 19.

Try it in the browser and in the terminal

A GET route can be opened in the browser address bar: http://localhost:5000/api/health. For everything else use curl or a tool such as the REST Client (lesson 13). curl -i http://localhost:5000/api/hello/Asha?shout=true shows the whole response including the headers.

Your task

  1. Run npm install inside server (it now installs Express), then npm run dev.
  2. Open /api/health in the browser. Reload and watch time change.
  3. Call /api/hello/Asha and /api/hello/Asha?shout=true.
  4. Add a route GET /api/add/:a/:b that returns the sum. Remember that parameters are text: convert with Number(), and answer 400 if either is not a number.

How to check it worked

  • /api/health returns { "ok": true, "time": ... }.
  • shout=true makes the message upper case.
  • Your add route answers 400 for /api/add/x/2.

Summary

  • app.get(path, (req, res) => ...) registers a route; res.json() answers.
  • Parameters (req.params) identify; query strings (req.query) adjust. Both arrive as text.
  • Routes are checked in order. Put specific paths before general ones.

Download the solution (mern-lesson-09.zip)

Frequently asked questions

What is the difference between req.params and req.query?

req.params holds values from the path itself (/jobs/:id). req.query holds values after the question mark (?page=2). Both are text, so convert numbers yourself.

Do I need Express, or can I use plain Node.js?

You can use plain Node.js, as lesson 5 showed, but you would write much more code for routing, parsing and errors. Express is small, popular and well documented, which is why most Node.js tutorials and jobs use it.

Why does my change not appear?

Node.js reads files once at start. Use npm run dev, which runs node --watch and restarts for you. If you edited .env, restart by hand because it is not watched.