Your First Express Server

beginner
10 min

Your First Express Server

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.

The Anatomy of a Minimal Server

Here is the complete program again:

js
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.

Requests and Responses

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.

Ways to Respond

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:

js
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>'); });

Status Codes Matter

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:

js
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.

Handling Unknown Routes

If no route matches, Express sends a plain 404 page. You can customize this by adding a catch-all handler after all other routes:

js
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.

Choosing a Port

Hard-coding 3000 is fine locally, but hosting platforms inject the port through an environment variable. The standard pattern is:

js
const PORT = process.env.PORT || 3000;

Adopt this habit now and deployment later in the series will be painless.

Key Takeaways

  • express() creates the app object; app.listen starts the HTTP server.
  • Route handlers receive req (what the client sent) and res (how you reply).
  • Use res.json for APIs, res.send for quick text or HTML, and res.status to set codes.
  • Every request must get exactly one response.
  • Read the port from process.env.PORT with a local fallback.

Next lesson: Routing in Express, where we handle multiple paths and HTTP methods like POST, PUT, and DELETE.