August 1, 2025 • By Shakib Khan
In today's development landscape, most Node.js developers rely on frameworks like Express.js to create servers efficiently. However, in this article, we will take a step back from abstraction and explore the core of Node.js by building a server from scratch — without using any external frameworks. This deep dive into a raw Node.js server will help you understand how things work under the hood.
If you're reading this article, it likely means you're already familiar with Node.js and have it installed on your system. So, without covering the basics, we'll jump straight into the core coding aspects of building a raw Node.js server.
For now, all you need to do is create a folder and then create a file named server.js
inside it. Once that's done, you're ready to start coding.
mkdir raw-node-server cd raw-node-server touch server.js // If you are using linux or git bash // You can also create this folder & file using desktop ui 😂
mkdir raw-node-server cd raw-node-server touch server.js // If you are using linux or git bash // You can also create this folder & file using desktop ui 😂
First, we need to import something from the 'http' package. It's built-in package, so we don't need to install it
const { createServer } = require("http");
const { createServer } = require("http");
Next, we need to create a server using the createServer
function.
const server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/html" }); // 200 is the status code & Content-Type means what type of content we are sending from the server res.end("<h1>Hello World</h1>"); });
const server = createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/html" }); // 200 is the status code & Content-Type means what type of content we are sending from the server res.end("<h1>Hello World</h1>"); });
We've set up the server, and most of the work is done. But will it run? No, because it's not listening on any port yet. So, we need to make it listen to a port to start working.
server.listen(3000, () => { console.log("Server is running on port 3000"); });
server.listen(3000, () => { console.log("Server is running on port 3000"); });
After that, run the code, and your server will start working.
node server.js
node server.js
If you've done all these steps correctly, it means you've created a basic Node.js server without using any extra frameworks.
Now, let's add some routes.
const server = createServer((req, res) => { if (req.url === "/") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>We are on the home page</h1>"); } if (req.url === "/about") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>We are on the about page</h1>"); } else { res.writeHead(404, { "Content-Type": "text/html" }); res.end("<h1>404 Not Found</h1>"); } });
const server = createServer((req, res) => { if (req.url === "/") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>We are on the home page</h1>"); } if (req.url === "/about") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>We are on the about page</h1>"); } else { res.writeHead(404, { "Content-Type": "text/html" }); res.end("<h1>404 Not Found</h1>"); } });
We can add routes using if-else
statements. So now, you're someone who knows how to set up routes in a raw Node.js server.
To do that, we need to import another built-in module called fs
, which stands for File System.
const fs = require("fs");
const fs = require("fs");
const { createServer } = require("http"); const fs = require("fs"); const server = createServer((req, res) => { if (req.url === "/") { // New Added const homePage = fs.readFileSync("index.html", "utf-8"); res.writeHead(200, { "Content-Type": "text/html" }); res.end(homePage); // Ending } }); server.listen(3000, () => { console.log("Server is running on port 3000"); });
const { createServer } = require("http"); const fs = require("fs"); const server = createServer((req, res) => { if (req.url === "/") { // New Added const homePage = fs.readFileSync("index.html", "utf-8"); res.writeHead(200, { "Content-Type": "text/html" }); res.end(homePage); // Ending } }); server.listen(3000, () => { console.log("Server is running on port 3000"); });
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Cool Website</title> </head> <body> <h1>Cool Contents</h1> </body> </html>
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Cool Website</title> </head> <body> <h1>Cool Contents</h1> </body> </html>
node server.js
node server.js
We are using the fs
module to read an HTML file.
const { createServer } = require("http"); const server = createServer((req, res) => { if (req.url === "/") { const data = { success: true, message: "We are on the home page" }; // JSON data res.writeHead(200, { "Content-Type": "application/json" }); // Status code 200 & Content type json res.end(JSON.stringify(data)); } }); server.listen(3000, () => { console.log("Server is running on port 3000"); });
const { createServer } = require("http"); const server = createServer((req, res) => { if (req.url === "/") { const data = { success: true, message: "We are on the home page" }; // JSON data res.writeHead(200, { "Content-Type": "application/json" }); // Status code 200 & Content type json res.end(JSON.stringify(data)); } }); server.listen(3000, () => { console.log("Server is running on port 3000"); });
node server.js
node server.js
Now, if you open your browser and go to http://localhost:3000, you will see a JSON response.
Have you noticed that we use different content types when sending different kinds of content?
res.writeHead(200, { "Content-Type": "text/plain" }); // when you are serving plain text res.writeHead(200, { "Content-Type": "text/html" }); // when you are serving html res.writeHead(200, { "Content-Type": "text/css" }); // when you are serving css res.writeHead(200, { "Content-Type": "application/json" }); // when you are serving json data
res.writeHead(200, { "Content-Type": "text/plain" }); // when you are serving plain text res.writeHead(200, { "Content-Type": "text/html" }); // when you are serving html res.writeHead(200, { "Content-Type": "text/css" }); // when you are serving css res.writeHead(200, { "Content-Type": "application/json" }); // when you are serving json data
What is a status code?
There are different types of status codes. For example:
There are many more. You can learn more about status codes here:
I think this is enough for now. Today, we learned a lot about building a raw Node.js server without using any external packages.
If you have any questions or notice any mistakes, please feel free to leave a comment below. Your feedback is highly appreciated!
See you in the next article!
Programs must be written for people to read, and only incidentally for machines to execute. — Harold Abelson, computer scientist and co-author of Structure and Interpretation of Computer Program