Title: Building Web Servers with Node.js
In this chapter, we'll explore how to build web servers using Node.js. We'll start with the basics of the HTTP module, learn how to handle requests and responses, and dive into advanced topics like HTTPS and routing.
A. Introduction to the HTTP Module
The HTTP module in Node.js is used to create web servers and handle HTTP requests and responses. It’s a core module, meaning you don't need to install any external packages to use it.
-
Loading the HTTP Module:
const http = require("http"); -
Key Features:
- Create web servers that can handle HTTP requests and send responses.
- Easily manage request data, such as headers and body content.
- Integrate with other Node.js modules to serve static files, handle routing, and more.
B. Creating a Simple HTTP Server
Creating a basic HTTP server in Node.js is straightforward. Here’s how to get started:
-
Basic Server Setup:
const http = require("http"); const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader("Content-Type", "text/plain"); res.end("Hello, World!\n"); }); const PORT = 3000; server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}/`); }); -
Explanation:
- The
http.createServer()method creates a new server instance. - The callback function takes two arguments:
req(request) andres(response). - The server listens on the specified port (3000 in this case) and responds with "Hello, World!" to every incoming request.
- The
C. Handling HTTP Requests and Responses
Node.js provides tools to handle various types of HTTP requests and send appropriate responses.
-
Reading Request Data:
- You can access request headers, method, and URL directly from the
reqobject.
const server = http.createServer((req, res) => { console.log(`Request Method: ${req.method}`); console.log(`Request URL: ${req.url}`); res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify({ message: "Data received" })); }); - You can access request headers, method, and URL directly from the
-
Sending Responses:
- You can set the status code, headers, and body content using the
resobject.
res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify({ message: "Hello, JSON!" })); - You can set the status code, headers, and body content using the
D. Routing Basics with the HTTP Module
Routing is the process of directing incoming requests to the appropriate handlers based on the request URL.
-
Simple Routing:
const server = http.createServer((req, res) => { if (req.url === "/") { res.statusCode = 200; res.setHeader("Content-Type", "text/plain"); res.end("Home Page\n"); } else if (req.url === "/about") { res.statusCode = 200; res.setHeader("Content-Type", "text/plain"); res.end("About Page\n"); } else { res.statusCode = 404; res.setHeader("Content-Type", "text/plain"); res.end("Not Found\n"); } }); -
Explanation:
- Routes are defined using conditional statements (
if-else). - Based on the URL (
req.url), the server responds with different content.
- Routes are defined using conditional statements (
E. Serving Static Files with HTTP
Serving static files like HTML, CSS, and JavaScript is essential for most web servers.
-
Serving Static Files:
const fs = require("fs"); const path = require("path"); const server = http.createServer((req, res) => { const filePath = path.join( __dirname, "public", req.url === "/" ? "index.html" : req.url ); const extname = String(path.extname(filePath)).toLowerCase(); const mimeTypes = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".png": "image/png", ".jpg": "image/jpg", ".wav": "audio/wav", }; const contentType = mimeTypes[extname] || "application/octet-stream"; fs.readFile(filePath, (error, content) => { if (error) { if (error.code === "ENOENT") { fs.readFile("./404.html", (err, content) => { res.writeHead(404, { "Content-Type": "text/html" }); res.end(content, "utf-8"); }); } else { res.writeHead(500); res.end(`Sorry, there was an error: ${error.code} ..\n`); } } else { res.writeHead(200, { "Content-Type": contentType }); res.end(content, "utf-8"); } }); }); server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}/`); }); -
Explanation:
- The
fs.readFile()method is used to read the requested file. - The content type is determined based on the file extension.
- The server responds with the file content or a 404 error if the file is not found.
- The
F. Advanced HTTP Features: HTTPS, HTTP2
Node.js also supports more advanced HTTP features like HTTPS and HTTP2.
-
HTTPS:
- HTTPS adds a layer of security using SSL/TLS.
- To create an HTTPS server, you need an SSL certificate.
const https = require("https"); const fs = require("fs"); const options = { key: fs.readFileSync("server.key"), cert: fs.readFileSync("server.cert"), }; https .createServer(options, (req, res) => { res.writeHead(200); res.end("Hello, Secure World!\n"); }) .listen(443); -
HTTP2:
- HTTP2 offers performance improvements over HTTP1.1 by multiplexing multiple streams over a single connection.
const http2 = require("http2"); const fs = require("fs"); const server = http2.createSecureServer({ key: fs.readFileSync("server.key"), cert: fs.readFileSync("server.cert"), }); server.on("stream", (stream, headers) => { stream.respond({ "content-type": "text/html", ":status": 200, }); stream.end("<h1>Hello HTTP2!</h1>"); }); server.listen(443);
G. Handling Requests and Responses
Handling various types of requests and managing responses efficiently is critical for building robust web servers.
-
Handling GET and POST Requests:
const server = http.createServer((req, res) => { if (req.method === "GET") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>GET Request Received</h1>"); } else if (req.method === "POST") { let body = ""; req.on("data", (chunk) => { body += chunk.toString(); }); req.on("end", () => { res.writeHead(200, { "Content-Type": "text/html" }); res.end(`<h1>POST Request Received with Data: ${body}</h1>`); }); } }); -
Explanation:
- The server can distinguish between different HTTP methods (
GET,POST, etc.) usingreq.method. - For
POSTrequests, data is collected from the request body before responding.
- The server can distinguish between different HTTP methods (
H. Working with Query Parameters and Request Bodies
Handling query parameters and request bodies is essential for dynamic content.
-
Query Parameters:
const url = require("url"); const server = http.createServer((req, res) => { const queryObject = url.parse(req.url, true).query; res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(queryObject)); }); -
Request Bodies:
const server = http.createServer((req, res) => { if (req.method === "POST") { let body = ""; req.on("data", (chunk) => { body += chunk.toString(); }); req.on("end", () => { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ receivedData: body })); }); } });
I. Serving Static Files
Serving static files like HTML, CSS, JavaScript, and images is fundamental in web development.
-
Serving HTML Files:
const fs = require("fs"); const path = require("path"); const server = http.createServer((req, res) => { const filePath = path.join( __dirname, "public", req.url === "/" ? "index.html" : req.url ); fs.readFile(filePath, (err, content) => { if (err) { res.writeHead(404, { "Content-Type": "text" }); res.end("<h1>404 Not Found</h1>"); } else { res.writeHead(200, { "Content-Type": "text/html" }); res.end(content); } }); }); server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}/`); }); -
Explanation:
-
The server reads the requested file from the file system and serves it.
-
If the file is not found, a 404 error page is displayed.
J. Implementing Simple Routing
Routing is key to directing users to different parts of your website or application.
- Basic Routing Example:
const server = http.createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<h1>Home Page</h1>");
} else if (req.url === "/about") {
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<h1>About Us</h1>");
} else {
res.writeHead(404, { "Content-Type": "text/html" });
res.end("<h1>404 Not Found</h1>");
}
});
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});
- Explanation:
- Different URLs are handled by checking the
req.urlvalue. - Each route returns different HTML content.
- Different URLs are handled by checking the
Conclusion
Building web servers with Node.js is a powerful way to serve dynamic content on the web. By mastering the HTTP module and understanding how to handle requests, responses, and routing, you can create robust web applications. This chapter equips you with the foundational knowledge to start building your own web servers and expand into more advanced topics like HTTPS and HTTP2.