Nodejs Core Project for beginners
folder/storage folder/config.js folder/crypto.js folder/events.js folder/server.js folder/utils.js
This project demonstrates how several Node.js core modules work together to build a simple file server without Express or other external packages.
The main modules used are:
http— creates the web server and handles HTTP requests/responsesfs— reads, writes, lists, and deletes filespath— safely works with file and directory pathsevents— allows different parts of the application to communicate through eventsstream— processes data in small chunks instead of loading entire files into memoryBuffer— represents raw binary datacrypto— calculates SHA-256 hashes for files
CLIENT
Browser / curl
│
│ HTTP Request
▼
┌──────────────┐
│ HTTP │
│ Server │
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
PATH FS EVENTS
│ │ │
│ ▼ │
│ File System │
│ │ │
│ ▼ │
│ STREAM │
│ │ │
│ ▼ │
│ BUFFER │
│ │ │
│ ▼ │
│ CRYPTO │
│ │ │
└────────────┼────────────┘
│
▼
RESPONSE
│
▼
CLIENT
The http module creates the web server.
const http = require("http");
const server = http.createServer((req, res) => {
// Handle request
});When a client sends a request, Node.js gives us two important objects:
req → Request from the client
res → Response sent back to the client
For example:
Client
│
│ GET /files
▼
Node.js HTTP Server
│
│ JSON response
▼
Client
The project does not use Express. The HTTP server is created directly with Node.js.
The path module helps us work with filesystem paths.
const path = require("path");
const filePath = path.join(
STORAGE_DIR,
filename
);Instead of manually creating paths with strings, path.join() builds the path correctly for the operating system.
We also use:
path.basename()to get only the filename.
For example:
../../secret.txt
can be reduced to:
secret.txt
This helps prevent a user from supplying a filename containing directory traversal such as:
../../
The basic relationship is:
User filename
│
▼
path.basename()
│
▼
Safe filename
│
▼
path.join()
│
▼
Full file path
The fs module stands for File System.
It allows Node.js to interact with files and directories.
This project uses it to:
- Create the storage directory
- List files
- Check whether files exist
- Read file information
- Create read streams
- Create write streams
- Delete files
Example:
const fs = require("fs");
const files = fs.readdirSync(STORAGE_DIR);For large files, we use streams instead of reading the entire file at once:
const readStream =
fs.createReadStream(filePath);and:
const writeStream =
fs.createWriteStream(filePath);This connects fs directly with the stream module.
Streams allow data to be processed gradually.
Imagine a 1 GB file.
Without a stream, you could think of the process as:
1 GB File
│
▼
Load everything
│
▼
RAM
With a stream:
1 GB File
│
├── Chunk
├── Chunk
├── Chunk
├── Chunk
└── ...
│
▼
Process
The entire file does not need to be loaded into memory at once.
The project uses:
fs.createReadStream()for reading files and:
fs.createWriteStream()for writing files.
We also use:
pipeline()to connect streams together.
For example, downloading a file:
File
│
▼
Read Stream
│
▼
Buffer Chunks
│
▼
HTTP Response
│
▼
Client
A Buffer represents raw binary data.
For example:
const data = Buffer.from("Hello");
console.log(data);A Buffer contains bytes rather than normal JavaScript strings.
Streams commonly provide data as Buffers:
stream.on("data", (chunk) => {
console.log(
Buffer.isBuffer(chunk)
);
});The result is:
true
So when a file is streamed, the relationship is:
File
│
▼
Stream
│
▼
Buffer
│
▼
Process the bytes
This is especially important when working with:
- Images
- Videos
- PDFs
- ZIP files
- Audio
- Executables
- Other binary files
The crypto module provides cryptographic functionality.
This project uses it to calculate a SHA-256 hash.
const crypto = require("crypto");
const hash =
crypto.createHash("sha256");The file is read as a stream:
const stream =
fs.createReadStream(filePath);Each Buffer is then passed into the hash:
stream.on("data", (chunk) => {
hash.update(chunk);
});When the file is completely processed:
const result =
hash.digest("hex");The complete process is:
File
│
▼
fs.createReadStream()
│
▼
Stream
│
├── Buffer
├── Buffer
├── Buffer
└── Buffer
│
▼
crypto.createHash("sha256")
│
▼
hash.update(chunk)
│
▼
hash.digest()
│
▼
SHA-256 Hash
This means we can calculate the hash without loading the entire file into memory.
Node.js uses an event-driven architecture.
This project creates its own EventEmitter:
const EventEmitter =
require("events");
const fileEvents =
new EventEmitter();We can listen for an event:
fileEvents.on(
"fileUploaded",
(filename) => {
console.log(
`File uploaded: ${filename}`
);
}
);Then another part of the application can trigger it:
fileEvents.emit(
"fileUploaded",
filename
);The idea is:
Upload happens
│
▼
emit("fileUploaded")
│
├──► Logger
├──► Statistics
└──► Other listeners
This allows different parts of the application to communicate without being tightly connected.
When a user uploads a file, several Node.js concepts work together.
CLIENT
│
│ POST /upload
▼
┌─────────────┐
│ HTTP │
│ Server │
└──────┬──────┘
│
▼
req
Readable Stream
│
▼
pipeline()
│
▼
Writeable Stream
│
▼
fs.create
WriteStream
│
▼
Storage File
│
▼
fileUploaded Event
│
▼
fs.createReadStream
│
▼
Buffer Chunks
│
▼
crypto SHA-256
│
▼
Hash
│
▼
HTTP Response
│
▼
CLIENT
When a user downloads a file:
CLIENT
│
│ GET /download/file.txt
▼
HTTP SERVER
│
▼
PATH
│
▼
Find safe file path
│
▼
FS
│
▼
createReadStream()
│
▼
Buffer Chunks
│
▼
HTTP Response
│
▼
CLIENT
The file is streamed instead of being unnecessarily loaded completely into memory.
The most important idea in this project is that these modules are not isolated.
They solve different parts of the same problem:
http
│
│ Handles communication
▼
stream
│
│ Moves data gradually
▼
Buffer
│
│ Represents raw bytes
▼
fs / crypto
│
├── fs → stores or reads data
│
└── crypto → processes data
Meanwhile:
path
│
▼
Determines where the file is
And:
events
│
▼
Notifies the application that something happened
Together:
HTTP
│
▼
STREAM
│
▼
BUFFER
/ \
/ \
▼ ▼
FS CRYPTO
│ │
▼ ▼
FILE HASH
│ │
└──────┬──────┘
▼
EVENTS
│
▼
RESPONSE
This project is intentionally built without Express or third-party packages.
The goal is to understand what Node.js is doing underneath frameworks.
When using Express, you might write:
app.get("/files", handler);But underneath the abstraction, Node.js still has to deal with things such as:
HTTP requests
HTTP responses
Streams
Buffers
Filesystem operations
Events
Paths
Binary data
Learning these core modules gives you a stronger understanding of what frameworks such as Express are actually built on top of.
By completing this project, you should understand:
- How Node.js creates an HTTP server
- What
reqandresrepresent - How HTTP requests can act as streams
- How files are read and written
- Why streams are useful for large files
- What a Buffer represents
- How streams produce Buffer chunks
- How cryptographic hashes process data
- How EventEmitters work
- How events allow different parts of an application to communicate
- How
pathhelps construct and sanitize file paths - How several Node.js core modules can work together
The main lesson is:
Node.js is not just about writing server routes. It provides low-level building blocks for handling network communication, files, data streams, binary data, events, and cryptography.
MIT
Deadcode