HTTP Server

1. Introduction to Node.js HTTP Server

What is an HTTP Server in Node.js?

In Node.js, an HTTP server is a program created using the built-in http module that allows your computer to receive requests from users (like a web browser) and send back responses. Unlike other languages where you might need external tools (like Apache or Nginx) to run a server, Node.js can create its own web server directly in your JavaScript code.

Why is this important?

  • Backend JavaScript: It enables developers to run JavaScript on the server side, meaning you can write both the frontend (what users see) and the backend (logic and data) in the same language.
  • Foundation for Web Apps: This basic server forms the foundation for building complex web applications, APIs, and is the underlying technology for popular frameworks like Express.

2. Core Concepts and Flow

  • Node.js Runtime: Node.js is a runtime environment built on Chrome's V8 engine. It is single-threaded and uses non-blocking I/O, which means it can handle many requests efficiently without stopping to wait for heavy tasks like file reading to finish.
  • HTTP Module: Node.js comes with a built-in library called http. We access it using require('http') to create servers without installing extra packages.
  • createServer Function: This method sets up the server. It takes a "callback function" (a request listener) that runs every time someone tries to access your server.
  • Request (req) and Response (res):
    • req (Request): Contains details about what the user is asking for, such as the URL they visited (req.url), their IP address, and headers.
    • res (Response): Used to send data back to the user. We use res.end() to finish the request and send the final message.
  • Port and Localhost: A port is like a specific "door" number on your computer (the server). localhost refers to your own machine. By listening on port 8000, we tell the server to wait for visitors specifically at that door.

Basic Flow:

  1. Create Server: You write code to initialize the server.
  1. Listen: The server waits at a specific port (e.g., 8000).
  1. Request: A client (browser) visits localhost:8000.
  1. Callback: Node.js triggers your function, providing req (details) and res (tool to reply).
  1. Response: You send a message back using res.end(), which the browser displays.

3. Code Examples

Example 1: Very Basic HTTP Server

This example creates a server that simply says "Hello from Server" to anyone who visits.

// 1. Import the built-in http module
const http = require("http");

// 2. Create the server// The callback function runs every time a request comes in
const myServer = http.createServer((req, res) => {
    console.log("New request received"); // Logs to your terminal
    
    // 3. Send a response back to the user
    res.end("Hello from Server"); 
});

// 4. Start listening on port 8000// The callback here runs once when the server successfully starts
myServer.listen(8000, () => {
    console.log("Server Started!");
});

Example 2: HTTP Server with Routing and Logging

This example logs every visit to a file named log.txt and shows different messages based on the URL (Routing).

const http = require("http");
const fs = require("fs"); // Import File System module for logging

const myServer = http.createServer((req, res) => {
    // Create a log entry with the current timestamp and the requested URL
    const logMessage = `${Date.now()}: ${req.url} New Request Received\n`;

    // 1. Non-blocking Logging// We use appendFile (async) instead of appendFileSync so we don't block other users
    fs.appendFile("log.txt", logMessage, (err, data) => {
        
        // 2. Basic Routing (Switch Case)// Decide what to show based on the URL the user asked for
        switch(req.url) {
            case "/":
                res.end("HomePage"); // Visiting localhost:8000/
                break;
            case "/about":
                res.end("I am Piyush Garg"); // Visiting localhost:8000/about
                break;
            default:
                res.end("404 Not Found"); // Any other URL
        }
    });
});

myServer.listen(8000, () => console.log("Server Started!"));

Why Non-blocking?

We use fs.appendFile (asynchronous) rather than fs.appendFileSync. If we used the synchronous (blocking) version, the server would stop and wait for the file to be written before handling any other user's request. This could slow down the application significantly if many users visit at once.

4. Real-Time / Real-World Use Cases

  • Building REST APIs: Servers are often used to send raw data (JSON) rather than text. For example, requesting /products might return a list of items for an e-commerce site.
  • Logging and Monitoring: As shown in the example, servers track who visits and when. This log.txt data helps developers debug issues or analyze traffic patterns (e.g., knowing which IP address sent a request).
  • Routing (Multi-page Handling): Real websites have an "About" page, a "Contact" page, etc. The server uses switch or if statements to check req.url and serve the correct content for each path.
  • Handling User Forms: When a user clicks "Submit" on a login form, the HTTP server receives that data, processes it, and logs the user in.

5. High-Level Diagram

The following chart illustrates the flow of a request in the Node.js server described above.

Node.js Request Handling Flow

  1. Client: The user types a URL into the browser.
  1. Network: The request travels to port 8000 on the server.
  1. Node Server: The http.createServer catches the request.
  1. Event Loop: Node.js assigns the request to the listener callback.
  1. Logic & Logging: The code appends data to a log file (non-blocking) and checks routing logic.
  1. Response: The server sends the final data back to the client via res.end().

6. Best Practices and Key Points

  • Always use Non-Blocking I/O: Avoid functions ending in Sync (like readFileSync) in your main request handler. Blocking operations freeze the single thread, making the server unresponsive for everyone else.
  • Naming Conventions: Name your entry file index.js. It is a standard practice developers look for to find the starting point of your application.
  • NPM Scripts: Configure a start script in your package.json (e.g., "start": "node index.js"). This allows you to run npm start to launch your server easily.
  • Restart on Changes: If you change code (e.g., the response message), you must restart the server (Ctrl/Command + C then npm start) for changes to take effect, unless you use a tool like nodemon.

7. Conclusion

In summary, a Node.js HTTP server is a powerful way to run JavaScript on the backend, allowing you to handle web requests directly without external server software. By using http.createServer, developers can access the req object to understand what the user wants and the res object to send a reply. We learned that servers listen on specific ports (like 8000) and can perform tasks like logging user activity to files. Crucially, because Node.js is single-threaded, we must use non-blocking operations for tasks like file writing to ensure the server remains fast and responsive for all users.