A progressive, hands-on Node.js repository that walks through foundational server-side JavaScript concepts — from raw HTTP server construction and custom logging to building a complete, file-persisted Todo application. Built entirely on Node.js built-in modules with no external framework dependencies.
This repository is structured as a series of self-contained modules, each targeting a distinct layer of backend engineering with Node.js. Rather than jumping straight to a framework, the codebase deliberately uses the native http, fs, and path modules to demonstrate what Express and similar frameworks abstract away. It culminates in a functional Todo application that serves static HTML, handles RESTful routes manually, and persists data to a local JSON file.
The project is suited for developers who want to understand what happens beneath the framework layer, or for reviewers who want to see evidence of first-principles backend thinking.
Nodejs/
├── Basics/ # Core Node.js fundamentals (modules, fs, events, etc.)
├── LOGGER/ # Custom request/event logger implementation
├── SERVER/ # Standalone HTTP server — routing, request parsing, response handling
├── TODO/ # Full Todo application: CRUD routes + static file serving
├── static/ # Static HTML assets served by the application
└── tasks.json # JSON flat-file data store for Todo tasks
| Directory | Role |
|---|---|
Basics/ |
Exploratory scripts covering Node.js core APIs: file system, event emitter, module system, streams, and the event loop |
LOGGER/ |
A hand-rolled logger module that intercepts and records request/response data — written without any third-party logging library |
SERVER/ |
A minimal HTTP server built using http.createServer() — demonstrates manual routing, response codes, content-type headers, and request body parsing |
TODO/ |
The primary application module — integrates routing, static file serving, and JSON file I/O to deliver a working task management system |
static/ |
HTML pages (UI layer) served directly by the Node.js server, covering the frontend of the Todo app |
tasks.json |
Flat-file JSON database; stores the list of tasks with their state (pending/completed) |
| Layer | Technology |
|---|---|
| Runtime | Node.js (LTS) |
| Language | JavaScript (ES6+) |
| HTTP Layer | Node.js built-in http module |
| File I/O | Node.js built-in fs module (synchronous and async) |
| Path Handling | Node.js built-in path module |
| Data Persistence | JSON flat-file (tasks.json) |
| Frontend | Plain HTML (served statically) |
| Logging | Custom implementation (no winston/morgan dependency) |
| Package Manager | npm (no external runtime dependencies inferred) |
Note: No
package.jsonis present at the repository root, which confirms the application is intentionally dependency-free at runtime. All functionality is implemented using Node.js built-in modules only.
- Zero-dependency HTTP server — built with
http.createServer(), demonstrating raw request/response lifecycle management without Express - Manual routing — URL and method matching handled with explicit conditional logic, giving full visibility into how routing frameworks work under the hood
- Static file serving — HTML assets from the
static/folder are served by reading files from disk viafsand setting appropriateContent-Typeheaders - Todo CRUD operations — create, read, update (toggle), and delete tasks through manually defined route handlers
- JSON file persistence —
tasks.jsonacts as the data layer; the server reads from and writes to it on each operation using thefsmodule - Custom logger — a standalone logger module in
LOGGER/that records incoming requests, useful for debugging without attaching a third-party library - Progressive module structure — codebase is organized pedagogically: from
Basics/→LOGGER/→SERVER/→TODO/, each building on the previous
Client (Browser)
│
▼
Node.js HTTP Server (http.createServer)
│
├──► Static Route? → Read file from /static → Send HTML response
│
├──► GET /tasks → Read tasks.json → Send JSON array
├──► POST /tasks → Parse request body → Append to tasks.json → 201 response
├──► PATCH /tasks/:id → Read → Find by ID → Toggle status → Write → 200 response
└──► DELETE /tasks/:id → Read → Filter out by ID → Write → 200 response
Since there is no database, all persistence goes through tasks.json:
Request arrives
│
▼
fs.readFile('tasks.json') ← Deserialize current state
│
▼
Apply mutation (add/update/delete)
│
▼
fs.writeFile('tasks.json') ← Serialize updated state
│
▼
Send HTTP response
The LOGGER/ module is designed to be pulled into the server as a utility. It intercepts request metadata (method, URL, timestamp) and writes it to the console or a log output, inserted before the main route-handling logic.
Basics/ → Learn: require, fs, events, path
LOGGER/ → Apply: custom function that wraps request logging
SERVER/ → Build: createServer, routing, response headers
TODO/ → Integrate: routing + fs I/O + static serving = full app
- Node.js v16 or higher
- npm (optional — no
npm installrequired if no dependencies are present)
Verify your Node.js installation:
node --version# Clone the repository
git clone https://github.com/CoderNived/Nodejs.git
# Navigate into the project
cd NodejsNo npm install step is needed if the project uses only built-in Node.js modules.
Each module can be run independently:
# Explore Basics
node Basics/<filename>.js
# Run the standalone HTTP server
node SERVER/<entry-file>.js
# Run the Logger module
node LOGGER/<entry-file>.js
# Run the full Todo application
node TODO/<entry-file>.jsReplace
<entry-file>with the actual entry point filename (e.g.,index.js,app.js, orserver.js) found inside each directory.
Once the Todo server is running, open your browser at:
http://localhost:<PORT>
The port number will be logged to the console on startup.
Based on the visible structure, the project does not appear to use a .env file. Configuration values such as port numbers are likely hardcoded as constants in the server entry file.
If you extend the project to use environment variables, a recommended .env.example would be:
PORT=3000
DATA_FILE=./tasks.jsonThe following endpoints are inferred from the TODO/ module's purpose and the tasks.json data store. Exact route strings may vary from the source code.
| Method | Route | Purpose | Auth Required |
|---|---|---|---|
GET |
/ |
Serve the main HTML page (index) | No |
GET |
/tasks |
Retrieve all tasks as JSON array | No |
POST |
/tasks |
Create a new task | No |
PATCH |
/tasks/:id |
Toggle task completion status | No |
DELETE |
/tasks/:id |
Delete a task by ID | No |
Disclaimer: These routes are inferred from the project's structure and
tasks.jsondata file. Refer to the actual source files inTODO/for the exact implementation.
Request:
GET /tasks HTTP/1.1
Host: localhost:3000Response:
[
{ "id": 1, "title": "Buy groceries", "status": false },
{ "id": 2, "title": "Read documentation", "status": true }
]Request:
POST /tasks HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
"title": "Write unit tests"
}Response:
{
"id": 3,
"title": "Write unit tests",
"status": false
}Status: 201 Created
Request:
DELETE /tasks/2 HTTP/1.1
Host: localhost:3000Response: 200 OK or 204 No Content
Without a framework's built-in error middleware, error handling in a vanilla Node.js server typically follows one of these patterns:
- 404 Not Found — If no route matches the incoming URL, the server sends a
404response with a plain-text or JSON error message - 405 Method Not Allowed — If the URL matches but the HTTP method does not, a
405is returned - 500 Internal Server Error — If
fs.readFileorfs.writeFilethrows, the error is caught and a500response is emitted - Malformed JSON — POST body parsing wraps
JSON.parsein a try/catch to handle bad input gracefully
All error responses are expected to include a descriptive message body:
{ "error": "Task not found" }This project does not implement authentication or authorization — it is a learning-focused implementation with no protected routes or session management.
Noted absence of:
- JWT or session-based auth
- Password hashing
- Input sanitization middleware
- CORS headers
- HTTPS/TLS configuration
These are deliberate omissions for a fundamentals-focused repository. A production version would require all of the above.
- No framework = full transparency. Every line that handles a request, parses a body, or sends a response is written explicitly. This is a feature for learning, not a limitation.
- Synchronous vs. async
fs. Depending on the implementation,fs.readFileSyncmay be used for simplicity. In production, async reads with proper callback or Promise handling (fs.promises) are preferred to avoid blocking the event loop. - ID generation. Since there's no database, task IDs are likely generated by incrementing the current max ID in the array or using
Date.now(). This is fine for a local application but would need a proper UUID strategy at scale. - tasks.json as a database. This is a classic beginner-to-intermediate pattern. It works for single-user, single-process scenarios but is not concurrency-safe and does not scale horizontally.
- Static serving without a CDN. All HTML is read from disk on every request. A production app would serve static assets via a CDN or a dedicated static file server like nginx.
These are realistic next steps a senior engineer would suggest, in order of priority:
- Replace JSON file storage with SQLite or MongoDB. A lightweight database removes the concurrency risk and gives proper query capabilities without a heavy infrastructure dependency.
- Introduce Express.js. Once the raw HTTP fundamentals are understood, Express dramatically reduces boilerplate and makes the routing logic declarative and maintainable.
- Add input validation. Validate incoming request bodies (e.g., ensure
titleis a non-empty string) before writing to the data store. Libraries likezodorjoimake this ergonomic. - Write tests. Add unit tests for route handlers and the logger module using
node:test(built-in since Node.js v18) or Jest. - Add proper error logging. The custom LOGGER could be extended to write timestamped entries to a
.logfile for persistence across sessions. - Introduce environment-based configuration. Move port numbers and file paths into a
.envfile and useprocess.envto read them — a basic but important production habit. - Add a proper frontend build. The HTML in
static/is currently served raw. A simple build step (even just a bundler like Vite for local dev) would allow the frontend to grow without becoming unmanageable. - Containerize with Docker. A minimal
Dockerfilewould make the application portable and deployment-ready.
This repository is a clean, deliberate journey through the parts of Node.js that most developers skip when they jump straight to Express. By building an HTTP server, a logger, and a data-persisted Todo application from scratch using only built-in modules, it demonstrates a genuine understanding of how the Node.js runtime works — not just how to use the tools built on top of it. That foundational fluency is what separates developers who can debug a framework from developers who can only use one.
This project is open source. No license file is currently present in the repository.