Node.js HTTP Server Implementation from Scratch

Creating an HTTP server from scratch in Node.js allows you to understand its core capabilities and provides fine-grained control over the server’s behavior. Here’s a step-by-step guide:

Step 1: Set Up Your Project

  1. Initialize a New Node.js Project:
mkdir node-http-server
cd node-http-server
npm init -y
  1. Install Dependencies (if needed): For a basic server, no additional packages are required.

Step 2: Create the HTTP Server

  1. Import the http Module: Node.js provides a built-in http module for handling HTTP requests and responses.

  2. Write the Server Code: Create a file named server.js and include the following code:

const http = require("http");

// Define the server's behavior
const server = http.createServer((req, res) => {
    // Log the incoming request
    console.log(`${req.method} request for ${req.url}`);

    // Set the response headers
    res.writeHead(200, { "Content-Type": "text/plain" });

    // Send the response body
    res.end("Hello, World!");
});

// Start the server
const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Step 3: Run the Server

  1. Use the command below to start the server:
node server.js
  1. Open a browser and navigate to http://localhost:3000. You should see the message Hello, World!.

Step 4: Enhance the Server

Handle Different Routes

You can enhance the server to respond to different URLs:

const http = require("http");

const server = http.createServer((req, res) => {
    if (req.url === "/" && req.method === "GET") {
        res.writeHead(200, { "Content-Type": "text/html" });
        res.end("<h1>Welcome to the Home Page</h1>");
    } else if (req.url === "/about" && req.method === "GET") {
        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 - Page Not Found</h1>");
    }
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Handle POST Requests

To handle POST requests, you can parse the incoming data:

const http = require("http");

const server = http.createServer((req, res) => {
    if (req.url === "/data" && req.method === "POST") {
        let body = "";

        // Collect the data
        req.on("data", (chunk) => {
            body += chunk.toString();
        });

        // Respond after receiving all data
        req.on("end", () => {
            res.writeHead(200, { "Content-Type": "application/json" });
            res.end(JSON.stringify({ message: "Data received", data: body }));
        });
    } else {
        res.writeHead(404, { "Content-Type": "text/html" });
        res.end("<h1>404 - Page Not Found</h1>");
    }
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Step 5: Add Middleware-like Functionality

Although Node.js doesn’t have middleware by default, you can create similar functionality:

const http = require("http");

const middleware = (req, res, next) => {
    console.log(`Incoming request: ${req.method} ${req.url}`);
    next();
};

const server = http.createServer((req, res) => {
    middleware(req, res, () => {
        if (req.url === "/" && req.method === "GET") {
            res.writeHead(200, { "Content-Type": "text/html" });
            res.end("<h1>Welcome to the Home Page</h1>");
        } else {
            res.writeHead(404, { "Content-Type": "text/html" });
            res.end("<h1>404 - Page Not Found</h1>");
        }
    });
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Step 6: Graceful Shutdown

Handle server shutdown properly by catching termination signals:

const http = require("http");

const server = http.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Server is running");
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

process.on("SIGINT", () => {
    console.log("Shutting down the server...");
    server.close(() => {
        console.log("Server closed");
        process.exit(0);
    });
});

This implementation handles termination signals, ensuring that resources are cleaned up properly.

Conclusion

Building an HTTP server from scratch in Node.js is straightforward and offers immense flexibility. By following these steps, you can create a basic server and incrementally add functionality such as routing, request handling, and graceful shutdowns. For production-ready applications, consider frameworks like Express.js to simplify development and enhance maintainability.

Latest blog posts

Explore the world of programming and cybersecurity through our curated collection of blog posts. From cutting-edge coding trends to the latest cyber threats and defense strategies, we've got you covered.