WebNest
Team/Shahnawaz Sazid/Explore-Nodejs-Core-Modules

Repository

Explore-Nodejs-Core-Modules

View on GitHub ↗
JavaScript0 stars0 forks

README

In this module, you’ll start by understanding the event-driven heart of Node.js and explore how it handles tasks synchronously and asynchronously, especially when it comes to reading files, working with buffers, and streaming data efficiently.

But that’s just the beginning…

By the end of this module, you’ll be:

  • Building your own logger app to track events and activities
  • Navigating the filesystem using the Path module
  • Creating a basic ToDo app using Node’s native HTTP server
  • Implementing routing, setting custom response headers, and using Postman to test your API
  • Handling CRUD operations (Create, Read, Update, Delete) on ToDos with real HTTP requests—query params and all!

This module bridges the gap between learning and doing. It's where you transform knowledge into projects, gain confidence in Node.js fundamentals, and start thinking like a real backend developer.

Let’s go—your first full Node-powered app is just a few lessons away!

13-1 What is an event module?

  • Node.js Follows Event Driven Architecture.
  • Event Loop Plays an important role.
  • Event Loop Always Looks for if any event/ user request/ action is running or not.
  • Event Loop performs the task sending to thread pool. when task is done event loop send response through a callback function.
  • When its I/O Intensive task it sends to thread pool and when its cpu intensive task Its done in main thread.

Lets see in practical

  • In event loop we get event listener which listens all the events and performs tasks and send response.
  • We also get a event emitter which triggers the events.

Node.js Events

  • less play with this considering a school bell
// 📢 Step 1: Import the EventEmitter class from Node's built-in 'events' module
const EventEmitter = require("node:events");

// 🛒 Step 2: Bought a school bell from the shop by creating a class that can emit events
//           'SchoolBell' extends EventEmitter so it can emit and handle events like 'ring' or 'broken'
class SchoolBell extends EventEmitter {}

// 🔔 Step 3: Set up the bell (create an instance of the SchoolBell)
//           This 'schoolBell' is our event emitter (it will emit or broadcast events)
const schoolBell = new SchoolBell();

// 👂 Step 4: Students are now listening for the school bell to ring or break

// 📚 Listener 1: A student hears the bell ring and responds
schoolBell.on("ring", () => {
  console.log("🎒 Wow! Class session started!");
});

// 📚 Listener 2: Another student hears the bell ring and gives a different response
//               👉 This shows multiple listeners for the same event ('ring')
schoolBell.on("ring", () => {
  console.log("📓 Aha! Another class is coming up!");
});

// 😩 Listener 3: A student reacts when the bell is broken
//               👉 This shows we can have different types of events on the same emitter
schoolBell.on("broken", () => {
  console.log("😩 Will this class ever end?");
});

// 🏫 Step 5: The bell starts ringing and breaks

// 🔔 Emit the 'ring' event → all registered listeners for 'ring' will respond
schoolBell.emit("ring"); // Students react to the first bell ring

// 🔔 Emit 'ring' again → same listeners react again
schoolBell.emit("ring"); // Students react again for another bell

// 💥 Emit 'broken' event → only the listener for 'broken' responds
schoolBell.emit("broken"); // One student reacts to the broken bell
  • we can create multiple listener (ring, broken) on one event and the listeners will give different different callback response.
  • we can also create multiple event listener based on one event emitter like ring is returning two responses.
ConceptExample in CodeDescription
EmitterschoolBellAn object that triggers events (emit)
Event"ring", "broken"Named signals you emit
ListenerschoolBell.on("ring", () => { ... })A function that reacts to an event
Multiple ListenersTwo on("ring", ...)More than one response for a single event
Multiple Events"ring", "broken" on schoolBellOne emitter can have different events

use case of event module in node.js

  1. 🔄 Asynchronous Operations
  • File I/O: Emits events like open, data, end, error for non-blocking file handling.
  • HTTP Requests: Emits request, response to handle multiple web requests concurrently.
  • Streams: Emits data, end, error, finish while reading/writing data in chunks.
  1. 🧠 Custom Events
  • Application Logic: Trigger actions based on user interactions or app state changes.
  • Inter-Process Communication: Pass messages between services or app modules.
  • Modular Code: Decouple components for cleaner, maintainable architecture.
  1. 📦 EventEmitter Class
  • Core Class: Built-in EventEmitter from events module handles all event-based ops.

Methods:

  • .emit('event') – Trigger an event
  • .on('event', callback) – Listen for an event
  • .removeListener() / .removeAllListeners() – Clean up listeners
  • Async Support: Event handlers can be async for non-blocking behavior.

13-2 Synchronous way to read and write files

  • This file system means we can read the data from our machine where the files are stored.
  • We can also create new files in our machine and do data entry. And we can do update and delete the data.

Lets see how we can read the data from Machine using File system

Filesystem Of Node.js

  • we can read data in Synchronous way. here we will tell node.js files system the file path and tell him to grab the data. This operation will happen in Single Thread/ Main Thread because the synchronous works are not sent in thread pool. An this will block the single thread until the task is finished.

Synchronous File System Read

Read File sync Blog

fs.readFileSync(path[, options])
  • read file system
const fs = require("node:fs");
//  synchronous
const data = fs.readFileSync("./hello.txt", { encoding: "utf8" });
// if we do not giv the option it will show buffer <Buffer 48 65 6c 6c 6f 20 49 20 61 6d 20 52 65 61 64 69 6e 67 20 54 68 65 20 74 65 78 74>
console.log(data);
  • it will grab the text from the hello.txt file and show.

Synchronous File System Write

fs.writeFileSync(file, data[, options])

alt text

const fs = require("node:fs");

const text = "Learning File System";
fs.writeFileSync("./hello.txt", text);
const data = fs.readFileSync("./hello.txt", { encoding: "utf8" });
console.log(data);

Now Lets Understand how its blocking other process.

const fs = require("node:fs");

console.log("Task-1");
const text = "Learning File System";
fs.writeFileSync("./hello.txt", text);

console.log("Task-3");

const data = fs.readFileSync("./hello.txt", { encoding: "utf8" });
console.log("Task-4");
console.log(data);

Asynchronous File System

  • we can read data in Asynchronous way. This operation will happen in Thread Pool.File read --> single thread --> event loop --> thread pool --> finish task and send response

  • readFile works on asynchronous way by default.

13-3 Asynchronous way to read and write files

Asynchronous File System Read

Async Read

fs.readFile(path, options, callback);
  • As its asynchronous operation it will be solved by thread pool. so we need a callback here as well. Callback helps to send response of the complete task to user.

alt text

const fs = require("fs");

fs.readFile("example.txt", "utf8", (err, data) => {
  if (err) {
    console.error("Error reading file:", err);
    return;
  }
  console.log("File contents:", data);
});
  • This called error back pattern, if there is any error it will show the error.
  • now lets see our made asynchronous read example
const fs = require("node:fs");
let texts = "Default Text before set by callbacks";
console.log("Asyn Task-1");

fs.readFile("./hello.txt", { encoding: "utf8" }, (err, data) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }
  texts = data;
  console.log(texts, "Text Inside Callback");
});
console.log(texts);
console.log("Asyn Task-3");

Asynchronous File System Write

Write File Async

fs.writeFile(file, data, options, callback);
  • lets see wite and read of async
let texts = "Default Text before set by callbacks";
console.log("Asyn Task-1");

fs.writeFile("./hello.txt", texts, { encoding: "utf-8" }, (err) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }
  console.log("Written Successfully!");
});

fs.readFile("./hello.txt", { encoding: "utf-8" }, (err, data) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }
  texts = data;
  console.log(texts, "Text Inside Callback");
});
console.log(texts);
console.log("Asyn Task-3");

13-4 Buffer and Streaming

  • Streaming : Streaming is Process by using which we can transfer data from one place to another place.
  • Buffer : While doing streaming It is used to process a data piece by piece which is called buffer.

Lets understand using an example

  • Think of it like a pizza. we are slicing one by one and then giving everyone one by one.
  • In youtube we have full data/ video. The full video is not loaded at a time since it will take time to show. They came up with a solution like they will divide the pizza in different pieces and then each piece comes to user end one by one. For this reason which pice has came its showing to us by this time other pieces gets loaded. User Gets smooth experience.

Benefits of Streaming and Buffer.

  • Better In terms of User Experience
  • Needs short memory storage as it do not complete whole process at once. since ram memory is temporary and its short. some piece of data gets loaded and its kicked off and then other enters the temporary memory.

Different Types Of Streams

  1. Readable Stream : a stream where we can read data (ex. http req, fs.readStream)
  2. Writeable Stream : a stream where we can Write data (ex. http response, fs.writeStream)
  3. Duplex Stream : a stream for both read and write.
  4. Transform Stream : a stream where we can reshape data.

Lets see stream and buffer in practical

let texts = "Default Text before set by callbacks";
console.log("Asyn Task-1");

fs.writeFile("./hello.txt", texts, { encoding: "utf-8" }, (err) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }
  console.log("Written Successfully!");
});

fs.readFile("./hello.txt", { encoding: "utf-8" }, (err, data) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }
  texts = data;
  console.log(texts, "Text Inside Callback");
});
console.log(texts);
console.log("Asyn Task-3");
  • suppose we want this like we will read and immediately write. In this code case of asynchronous we can not take the data out of the block and cant store in variable. so we have to write inside callback

  • suppose we want to read the hello.txt data and make it duplicate and write inside hello-world.txt

const fs = require("node:fs");

fs.readFile("./hello.txt", { encoding: "utf-8" }, (err, data) => {
  if (err) {
    console.log("Opps!! Error Occurred.", err);
    return;
  }

  fs.writeFile("./hello-world.txt", data, { encoding: "utf-8" }, (err) => {
    if (err) {
      console.log("Opps!! Error Occurred.", err);
      return;
    }
    console.log("Written Successfully!");
  });
});
  • Here first the file is read fully and then write is done. as the data is not that big so its not issue here. but if there is a lot of data there will be a problem since we will cost time. we know read speed is faster than write speed. This will create a back pressure for large data and creating imbalance.

Here Streaming comes with a solution. read and write will be done in streaming format like duplex stream.

Files System Read Stream

Blog Of Read Stream

Blog Of Write Stream

fs.createReadStream(path, options);
fs.createWriteStream(path, options);

readStream.on();
const fs = require("node:fs");

const readStream = fs.createReadStream("./hello.txt", { encoding: "utf-8" });
const writeStream = fs.createWriteStream("./hello-world.txt", {
  encoding: "utf-8",
});

// as we have created event we will need a listener for the event
readStream.on("data");
  • as we have created readStream event we will need a listener for the event

  • when were are reading the data readStream event will be triggered.

  • While we are loading data the readStream will listen and will show the loaded piece of data.

    alt text

    alt text

  • When the event is data there will be function named listener. this function will give us the data.

    alt text

const fs = require("node:fs");

const readStream = fs.createReadStream("./hello.txt", { encoding: "utf-8" });
const writeStream = fs.createWriteStream("./hello-world.txt", {
  encoding: "utf-8",
});

readStream.on("data", (data) => {
  console.log(data);

  writeStream.write(data, (err) => {
    if (err) {
      console.log("Opps!! Error Occurred.", err);
      return;
    }
  });
});
  • here we are catching the error of write stream but we have nothing for catching the error of read stream.node.js have got us we can read stream and write stream error delicately.
const fs = require("node:fs");

const readStream = fs.createReadStream("./hello.txt", { encoding: "utf-8" });
const writeStream = fs.createWriteStream("./hello-world.txt", {
  encoding: "utf-8",
});
// readStream.on("eventName", callback);
readStream.on("data", (data) => {
  console.log(data);

  writeStream.write(data, (err) => {
    if (err) {
      console.log("Opps!! Error Occurred.", err);
      return;
    }
  });
});

readStream.on("error", (err) => {
  if (err) {
    throw Error("Error", err);
  }
});

// we have checked the write error but we can also check here like this.
writeStream.on("error", (err) => {
  if (err) {
    throw Error("Error", err);
  }
});
EventWhen it HappensCallback SignatureDescription
"data"When a chunk of data is available to read(chunk: string | Buffer)Emitted multiple times as data is streamed
"end"When there is no more data to read() => voidIndicates the stream has finished reading
"error"When an error occurs while reading or writing(err: Error) => voidAlways listen for this to avoid uncaught exceptions
"close"When the stream and underlying file descriptor are closed() => voidEmitted after end, when stream is completely cleaned up
"open"When the file descriptor is successfully opened(fd: number) => voidUseful for tracking access to the file descriptor
"pause"When the readable stream is paused() => voidHappens after calling .pause()
"resume"When the readable stream is resumed after being paused() => voidHappens after calling .resume()
"readable"When a readable stream has data available for manual .read() calls() => voidAllows for manually reading data in a loop
"ready"When the stream is ready for use() => voidEmitted only once, before any data events
"drain"When the write buffer becomes empty() => voidUsed in write streams to handle backpressure (after .write() returned false)
"finish"When all data has been flushed from the write stream() => voidEmitted after .end() is called
"pipe"When a readable stream is piped into a writable stream(src: Readable) => voidIndicates piping has started
"unpipe"When a readable stream is unpiped from a writable stream(src: Readable) => voidEmitted when .unpipe() is called
  • Now we want to show a success message while completed the write stream.
const fs = require("node:fs");

const readStream = fs.createReadStream("./hello.txt", { encoding: "utf-8" });
const writeStream = fs.createWriteStream("./hello-world.txt", {
  encoding: "utf-8",
});
readStream.on("data", (data) => {
  console.log(data);

  writeStream.write(data, (err) => {
    if (err) {
      console.log("Opps!! Error Occurred.", err);
      return;
    }
  });
});

readStream.on("error", (err) => {
  if (err) {
    throw Error("Error", err);
  }
});

readStream.on("end", () => {
  console.log("Reading Ended");
  writeStream.end();
});

writeStream.on("finish", () => {
  console.log("Writing Is Finish");
});

13-5 Making a basic logger app & Path module

  • so far we have learned IIFE. now we know how node.js uses IIFE and gives use access of some other method that are not present in global object.
  • We get __dirName, module, module.export, require etc.
  • We get another one named process. this gives us a lot of things in process. if we console it we can see.
  • Here is a magic inside process if we console console.log(process.argv). this gives us an array and the array shows us the location of node executable file and the location of the file we are running. alt text
  • we are telling that go the file location and run the file using the exe file.
  • If we run this node index.js Hello Shanda. it will show array and also capture the two words

alt text

  • suppose we do not want the path names we just want the words. we have to use slice method.
const inputArguments = process.argv.slice(2);

console.log(inputArguments);

alt text

  • we can bring them into single sentence using join
const text = inputArguments.join(" ");
console.log(text);
  • If there is no input we have to close the server. using process.exit(1)
if (!text) {
  console.log(
    "❌ Ballerina Kapuchina! Me me me me ! Please Provide a message To Log !"
  );
  console.log("Example : Node index.js Hellow World!");
  process.exit(1);
}
  • now lets make a place for storing the data we have gave.
const filePath = __dirname + "/log.txt";
// D:\WORK\renew-level-2\PH-MODULES\Be-An-Express-And-Mongoose-Master\Explore-Nodejs-Core-Modules\codes\logger-app/log.txt
// this will join the path thought there is wrong with "\" so we can use join.
  • concat will join the path but there ie a problem with "" so using path.join is better solution
const path = require("path");
const filePath = path.join(__dirname, "log.txt");

console.log(filePath); // D:\WORK\renew-level-2\PH-MODULES\Be-An-Express-And-Mongoose-Master\Explore-Nodejs-Core-Modules\codes\logger-app\log.txt
  • now place is created for storing the input values.
  • now we have to add the inputs with the existing data of log.txt file.
  • Asynchronously append data to a file, creating the file if it does not yet exist. data can be a string or a Buffer.

alt text

Final Version Of Logger

const path = require("path");

const fs = require("node:fs");

console.log(process.argv);

const inputArguments = process.argv.slice(2);

console.log(inputArguments);

const text = inputArguments.join(" ").concat("\n");

const timeStamp = new Date().toString();

console.log(timeStamp);

const message = `${text} ${timeStamp} \n`;

if (!message) {
  console.log(
    "❌ Ballerina Kapuchina! Me me me me ! Please Provide a message To Log"
  );
  console.log("Example : Node index.js Hellow World!");
  process.exit(1);
}
console.log(message);

// const filePath = __dirname + "/log.txt"
// D:\WORK\renew-level-2\PH-MODULES\Be-An-Express-And-Mongoose-Master\Explore-Nodejs-Core-Modules\codes\logger-app/log.txt
// this will join the path thought there is wrong with "\" so we can use join.
// console.log(filePath)

const filePath = path.join(__dirname, "log.txt");

console.log(filePath); // D:\WORK\renew-level-2\PH-MODULES\Be-An-Express-And-Mongoose-Master\Explore-Nodejs-Core-Modules\codes\logger-app\log.txt

fs.appendFile(filePath, message, { encoding: "utf-8" }, () => {
  console.log("Your Log Added Successfully!");
});
  • input
 Node index.js Hellow World!"

13-6 Creating a todo app with basic http server using nodejs

  • Lets create a server using node.js. For creating server we must connect with our machine network. and this connection is done using node.js HTTP Module.

HTTP Module

http.createServer([options][, requestListener])

http module createServer

  • Here we call http and give a callback function inside. This callback function handles request and response.
const http = require("node:http");

// Create a local server to receive data from
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(
    JSON.stringify({
      data: "Hello World!",
    })
  );
});

server.listen(8000);
  • lest create this server for our to do app.
const http = require("http");

const server = http.createServer((req, res) => {
  res.end("Welcome To Server");
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});
  • we can console.log the request and response. we will get after postman hit
const server = http.createServer((req, res) => {
  console.log({ req, res });
  res.end("Welcome To Server");
});
  • These will show all the details of the request and response having.
  • We will use some of them like res.end, req.headers

13-7 Routing In Node.js

  • lets see what important thing we get from request and response.
const server = http.createServer((req, res) => {
  // console.log({ req, res })

  console.log(req.url, req.method);
  res.end("Welcome To Server");
});
  • Output
/todos/update-todo
PATCH

Now Lets Define The Routes

  1. /todos - Get - All Todos
  2. /todos/create-todo - Post - create Todo
const http = require("http");

const server = http.createServer((req, res) => {
  // console.log({ req, res })

  if (req.url === "/todos" && req.method === "GET") {
    res.end("All Todos Here");
  }
  if (req.url === "/todos/create-todo" && req.method === "POST") {
    res.end("Todo Created");
  } else {
    res.end("Route Not Found");
  }
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});

13-8 Set response headers

  • When a request is sent then the server sends a response.
  • While sending the response we can send some meta data.
  • What is meta data? It means what will be the content type like json, or plain text or html.
Writing Inside Headers
  • We can send response status as well. For this we need to use res.writeHead(). this means we are trying to write the headers.
if (req.url === "/todos" && req.method === "GET") {
  res.writeHead(200, {
    "content-type": "text/plain",
    email: "ph@gmail.com",
  });

  res.end("All Todos ");
}

alt text

  • we can write the header in different was as well using res.setHeader(). but the writeHead is much cleaner.
if (req.url === "/todos" && req.method === "GET") {
  // res.writeHead(200, {
  //     "content-type": "text/plain",
  //     "email": "ph@gmail.com"
  // })
  res.statusCode = 200;
  res.setHeader("content-type", "text/plain");
  res.setHeader("email", "ph@gmail.com");
  res.end("All Todos ");
}
  • Now Lets see how we will handle json data inside header
const http = require("http");

const data = [
  {
    title: "Morning Routines",
    body: "Boost your day with simple habits.",
    createdAt: "2025-05-30T08:45:00Z",
  },
  {
    title: "AI in Life",
    body: "How AI is changing everything.",
    createdAt: "2025-05-29T17:20:00Z",
  },
];

const server = http.createServer((req, res) => {
  if (req.url === "/todos" && req.method === "GET") {
    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(JSON.stringify(data));
  } else {
    res.end("Route Not Found");
  }
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});
  • We can send html as well inside header.
if (req.url === "/todos" && req.method === "GET") {
  res.writeHead(200, {
    "content-type": "text/html",
  });
  res.end(`<h1> Hello I am HTmL</h1>`);
}
  • now we will use file system instead of fake data

13-9 Creating and Reading A ToDo using Postman

  • lets use our file system for database.

  • we will create indentation while doing stringify, i mean this will give space an something

fs.writeFileSync(filePath, JSON.stringify(parsedAllTodos, null, 2), {
  encoding: "utf-8",
});
res.end(JSON.stringify({ title, body, createdAt }, null, 2));
  • it will format the data

alt text

  • Final File System
const http = require("http");
const path = require("path");
const fs = require("fs");

const filePath = path.join(__dirname, "./db/todo.json");

const server = http.createServer((req, res) => {
  // get all todos
  if (req.url === "/todos" && req.method === "GET") {
    const data = fs.readFileSync(filePath, { encoding: "utf-8" });
    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(data);
  }
  // post a todo
  else if (req.url === "/todos/create-todo" && req.method === "POST") {
    let data = "";

    req.on("data", (chunk) => {
      data = data + chunk;
    });

    req.on("end", () => {
      const { title, body } = JSON.parse(data);
      console.log({ title, body });

      const createdAt = new Date().toLocaleString();
      const allTodos = fs.readFileSync(filePath, { encoding: "utf-8" });
      const parsedAllTodos = JSON.parse(allTodos);
      parsedAllTodos.push({ title, body, createdAt });
      fs.writeFileSync(filePath, JSON.stringify(parsedAllTodos, null, 2), {
        encoding: "utf-8",
      });
      res.end(JSON.stringify({ title, body, createdAt }, null, 2));
    });
  } else {
    res.end("Route Not Found");
  }
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});

13-10 Get Single ToDo with Query Params

const http = require("http");
const path = require("path");
const fs = require("fs");

const filePath = path.join(__dirname, "./db/todo.json");

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  console.log(url);

  const pathname = url.pathname;

  console.log(pathname);

  // get all todos
  if (pathname === "/todos" && req.method === "GET") {
    const data = fs.readFileSync(filePath, { encoding: "utf-8" });
    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(data);
  }
  // post a todo
  else if (pathname === "/todos/create-todo" && req.method === "POST") {
    let data = "";

    req.on("data", (chunk) => {
      data = data + chunk;
    });

    req.on("end", () => {
      const { title, body } = JSON.parse(data);
      console.log({ title, body });

      const createdAt = new Date().toLocaleString();
      const allTodos = fs.readFileSync(filePath, { encoding: "utf-8" });
      const parsedAllTodos = JSON.parse(allTodos);
      parsedAllTodos.push({ title, body, createdAt });
      fs.writeFileSync(filePath, JSON.stringify(parsedAllTodos, null, 2), {
        encoding: "utf-8",
      });
      res.end(JSON.stringify({ title, body, createdAt }, null, 2));
    });
  } else if (pathname === "/todo" && req.method === "GET") {
    const title = url.searchParams.get("title");
    console.log(title);
    const data = fs.readFileSync(filePath, { encoding: "utf-8" });
    const parsedData = JSON.parse(data);

    const todo = parsedData.find((todo) => todo.title === title);

    const stringifiedTodo = JSON.stringify(todo);

    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(stringifiedTodo);
  } else {
    res.end("Route Not Found");
  }
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});

13-11 Updating and Deleting Todo

  • Final Version Of Code.
const http = require("http");
const path = require("path");
const fs = require("fs");

const filePath = path.join(__dirname, "./db/todo.json");

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  console.log(url);

  const pathname = url.pathname;

  console.log(pathname);

  // get all todos
  if (pathname === "/todos" && req.method === "GET") {
    const data = fs.readFileSync(filePath, { encoding: "utf-8" });
    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(data);
  }
  // post a todo
  else if (pathname === "/todos/create-todo" && req.method === "POST") {
    let data = "";

    req.on("data", (chunk) => {
      data = data + chunk;
    });

    req.on("end", () => {
      const { title, body } = JSON.parse(data);
      console.log({ title, body });

      const createdAt = new Date().toLocaleString();
      const allTodos = fs.readFileSync(filePath, { encoding: "utf-8" });
      const parsedAllTodos = JSON.parse(allTodos);
      parsedAllTodos.push({ title, body, createdAt });
      fs.writeFileSync(filePath, JSON.stringify(parsedAllTodos, null, 2), {
        encoding: "utf-8",
      });
      res.end(JSON.stringify({ title, body, createdAt }, null, 2));
    });
  } else if (pathname === "/todo" && req.method === "GET") {
    const title = url.searchParams.get("title");
    console.log(title);
    const data = fs.readFileSync(filePath, { encoding: "utf-8" });
    const parsedData = JSON.parse(data);

    const todo = parsedData.find((todo) => todo.title === title);

    const stringifiedTodo = JSON.stringify(todo);

    res.writeHead(200, {
      "content-type": "application/json",
    });
    res.end(stringifiedTodo);
  } else if (pathname === "/todos/update-todo" && req.method === "PATCH") {
    const title = url.searchParams.get("title");

    let data = "";

    req.on("data", (chunk) => {
      data = data + chunk;
    });

    req.on("end", () => {
      const { body } = JSON.parse(data);
      const allTodos = fs.readFileSync(filePath, { encoding: "utf-8" });
      const parsedAllTodos = JSON.parse(allTodos);

      const todoIndex = parsedAllTodos.findIndex(
        (todo) => todo.title === title
      );

      parsedAllTodos[todoIndex].body = body;

      fs.writeFileSync(filePath, JSON.stringify(parsedAllTodos, null, 2), {
        encoding: "utf-8",
      });
      res.end(
        JSON.stringify(
          { title, body, createdAt: parsedAllTodos[todoIndex].createdAt },
          null,
          2
        )
      );
    });
  } else if (pathname === "/todos/delete-todo" && req.method === "DELETE") {
    const title = url.searchParams.get("title");
    const todos = JSON.parse(fs.readFileSync(filePath, "utf-8"));
    const index = todos.findIndex((t) => t.title === title);
    if (index === -1) {
      res.writeHead(404);
      return res.end("Todo not found");
    }
    const deletedTodo = todos.splice(index, 1)[0];
    fs.writeFileSync(filePath, JSON.stringify(todos, null, 2));
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ message: "Todo deleted", deletedTodo }));
  } else {
    res.end("Route Not Found");
  }
});

server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});
  • In practical we wont use the raw node.js

  • We use Express for it.

  • Proper Commented Version

// Import core Node.js modules
const http = require("http"); // For creating HTTP server
const path = require("path"); // For handling file paths
const fs = require("fs"); // For reading/writing JSON file

// Path to the JSON file storing todos
const filePath = path.join(__dirname, "./db/todo.json");

// Create the HTTP server
const server = http.createServer((req, res) => {
  // Parse incoming URL to extract pathname and query parameters
  const url = new URL(req.url, `http://${req.headers.host}`);
  console.log(url);

  const pathname = url.pathname;
  console.log(pathname);

  // GET all todos
  if (pathname === "/todos" && req.method === "GET") {
    const data = fs.readFileSync(filePath, { encoding: "utf-8" }); // Read todos from file
    res.writeHead(200, { "content-type": "application/json" }); // Set response header
    res.end(data); // Send the data
  }

  // POST a new todo
  else if (pathname === "/todos/create-todo" && req.method === "POST") {
    let data = "";

    // Collect data chunks from request
    req.on("data", (chunk) => {
      data += chunk;
    });

    // Once data is received completely
    req.on("end", () => {
      const { title, body } = JSON.parse(data); // Parse the JSON
      const createdAt = new Date().toLocaleString(); // Timestamp
      const allTodos = fs.readFileSync(filePath, "utf-8"); // Read existing todos
      const parsedAllTodos = JSON.parse(allTodos); // Parse JSON to array

      // Add new todo
      parsedAllTodos.push({ title, body, createdAt });

      // Write updated todos back to file
      fs.writeFileSync(
        filePath,
        JSON.stringify(parsedAllTodos, null, 2),
        "utf-8"
      );

      // Respond with the new todo
      res.end(JSON.stringify({ title, body, createdAt }, null, 2));
    });
  }

  // GET a specific todo by title (query param)
  else if (pathname === "/todo" && req.method === "GET") {
    const title = url.searchParams.get("title"); // Extract title from query
    console.log(title);
    const data = fs.readFileSync(filePath, "utf-8"); // Read all todos
    const parsedData = JSON.parse(data); // Parse to array

    const todo = parsedData.find((todo) => todo.title === title); // Find todo by title

    res.writeHead(200, { "content-type": "application/json" }); // Set header
    res.end(JSON.stringify(todo)); // Send the matched todo
  }

  // PATCH (update) a todo's body using title as identifier
  else if (pathname === "/todos/update-todo" && req.method === "PATCH") {
    const title = url.searchParams.get("title"); // Extract title from query
    let data = "";

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

    // Once data is fully received
    req.on("end", () => {
      const { body } = JSON.parse(data); // Extract new body
      const allTodos = fs.readFileSync(filePath, "utf-8");
      const parsedAllTodos = JSON.parse(allTodos);

      const todoIndex = parsedAllTodos.findIndex(
        (todo) => todo.title === title
      );

      // Update the todo body
      parsedAllTodos[todoIndex].body = body;

      // Write back the updated todos
      fs.writeFileSync(
        filePath,
        JSON.stringify(parsedAllTodos, null, 2),
        "utf-8"
      );

      // Respond with the updated todo
      res.end(
        JSON.stringify(
          {
            title,
            body,
            createdAt: parsedAllTodos[todoIndex].createdAt,
          },
          null,
          2
        )
      );
    });
  }

  // DELETE a todo by title (query param)
  else if (pathname === "/todos/delete-todo" && req.method === "DELETE") {
    const title = url.searchParams.get("title"); // Extract title from query
    const todos = JSON.parse(fs.readFileSync(filePath, "utf-8")); // Read and parse todos

    const index = todos.findIndex((t) => t.title === title); // Find index of todo

    // If not found, respond with 404
    if (index === -1) {
      res.writeHead(404);
      return res.end("Todo not found");
    }

    // Remove the todo and get deleted one
    const deletedTodo = todos.splice(index, 1)[0];

    // Save the updated todos
    fs.writeFileSync(filePath, JSON.stringify(todos, null, 2));

    // Respond with confirmation
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ message: "Todo deleted", deletedTodo }));
  }

  // Fallback route for unmatched paths
  else {
    res.end("Route Not Found");
  }
});

// Start server on localhost:5000
server.listen(5000, "127.0.0.1", () => {
  console.log("Server Is Listening");
});
← Back to profile