In the setup lesson you pasted a five-line server just to prove Express was installed. Now we slow down and truly understand it. By the end of this lesson you will know what the app object is, how a route handler receives requests, and how to respond with text, JSON, HTML, and proper status codes.
Here is the complete program again:
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('Hello from Express!');
});
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});Line by line: require('express') imports the framework and returns a factory function. Calling express() creates an application object, conventionally named app, which holds your routes and settings. app.get registers a handler for GET requests to the path '/'. Finally app.listen binds the app to a TCP port and starts accepting connections.
Every route handler receives two objects. The request object (req) describes what the client sent: the URL, headers, query string, and body. The response object (res) is your tool for replying. The cycle is simple: a request arrives, Express matches it to a handler, and your handler must eventually send exactly one response. If you never respond, the browser hangs; if you respond twice, Node throws an error about headers already being sent.
res.send is the Swiss army knife. Give it a string and Express sets Content-Type to text/html; give it an object and it serializes JSON. For APIs, prefer the explicit res.json:
app.get('/api/status', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime() });
});
app.get('/welcome', (req, res) => {
res.send('<h1>Welcome</h1><p>This is HTML.</p>');
});Browsers and API clients rely on HTTP status codes to know what happened. Express defaults to 200 OK, but you should be deliberate. Chain res.status before sending:
app.get('/api/secret', (req, res) => {
res.status(403).json({ error: 'Forbidden' });
});
app.post('/api/items', (req, res) => {
res.status(201).json({ message: 'Created' });
});The common ones to memorize: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 404 Not Found, and 500 Internal Server Error.
If no route matches, Express sends a plain 404 page. You can customize this by adding a catch-all handler after all other routes:
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});Order matters here: Express checks routes top to bottom, so the catch-all must be registered last. This ordering rule becomes central when we study middleware.
Hard-coding 3000 is fine locally, but hosting platforms inject the port through an environment variable. The standard pattern is:
const PORT = process.env.PORT || 3000;Adopt this habit now and deployment later in the series will be painless.
Next lesson: Routing in Express, where we handle multiple paths and HTTP methods like POST, PUT, and DELETE.