Difference between revisions of "Module5-NodeJS-TEST"
(Created page with "Node.js is a JavaScript runtime built on Chrome's V8 engine that allows JavaScript to run on the server. It is not a framework. Frameworks such as Express run on top of Node...") |
|||
| (8 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| − | Node.js is a JavaScript runtime built on Chrome's V8 engine that allows JavaScript to run on the server. | + | Node.js is a JavaScript runtime built on Chrome's V8 engine that allows JavaScript to run on the server. Frameworks such as Express run on top of Node.js. |
Node.js is especially useful for real-time, high-concurrency applications due to its non-blocking, event-driven execution model. | Node.js is especially useful for real-time, high-concurrency applications due to its non-blocking, event-driven execution model. | ||
| Line 13: | Line 13: | ||
print(data[0].title); | print(data[0].title); | ||
</source> | </source> | ||
| + | |||
| + | The above pseudo-code queries a database, saves the result of the query in a variable named data, and then prints out the first title in data. The problem arises when multiple clients all want to access the server at the same time. In a blocking I/O model, a client must wait until all other clients finish before it can start its process. With intensive operations and thousands of clients, this can easily crash your server! | ||
| + | |||
| + | The solution is to use non-blocking I/O. Consider this revised pseudo-code: | ||
Non-blocking example: | Non-blocking example: | ||
| Line 22: | Line 26: | ||
</source> | </source> | ||
| − | Modern Node. | + | |
| + | The revised pseudo-code achieves the same end result as the first version. However, ''it allows other clients to perform tasks while this process is waiting for the database to be queried!'' The code occurs in two parts: first, we start the query. Second, ''when the query is complete,'' our '''anonymous callback function''' is executed. In Node.js, callback functions are usually passed one or more parameters (often an error parameter and a result parameter). | ||
| + | |||
| + | Better yet, with non-blocking I/O, you avoid many of the concurrency headaches common in multi-threaded server environments, because JavaScript code in Node runs in a single main thread. Nifty! | ||
| + | |||
| + | '''Important clarification:''' | ||
| + | "Single-threaded" does not mean "no concurrency." Node can handle many concurrent connections. The difference is that it avoids running multiple threads of JavaScript at the same time, which reduces race conditions and locking complexity. The concurrency happens in the underlying event loop and operating system, not by spawning a new JavaScript thread per request. | ||
| + | |||
| + | === Modern Syntax: Promises and async/await === | ||
| + | |||
| + | Historically, Node relied heavily on callback functions, like the example above. While this model still works, large programs with many nested callbacks can become difficult to read and maintain (sometimes called "callback hell"). | ||
| + | |||
| + | Modern Node commonly uses '''Promises''' and the '''async/await''' syntax built on top of them. Conceptually, the behavior is identical: the operation begins, Node continues handling other work, and when the operation finishes, your code resumes. | ||
| + | |||
| + | For example: | ||
<source lang="javascript"> | <source lang="javascript"> | ||
| − | const data = await database.query("select * from news"); | + | async function getTitle() { |
| − | console.log(data[0].title); | + | const data = await database.query("select * from news"); |
| + | console.log(data[0].title); | ||
| + | } | ||
</source> | </source> | ||
| + | |||
| + | Behind the scenes, this is still non-blocking I/O. The `await` keyword does not freeze the entire server; it simply pauses that particular function until the Promise resolves, while the event loop continues processing other incoming requests. | ||
| + | |||
| + | So: | ||
| + | |||
| + | * Callbacks explain how Node originally structured asynchronous work. | ||
| + | * Promises formalize asynchronous behavior. | ||
| + | * async/await makes asynchronous code look more like synchronous code — but it remains non-blocking underneath. | ||
| + | |||
| + | Understanding the callback model helps you understand how Node works internally. Understanding async/await helps you write modern, readable Node applications. | ||
== Installing Node.js == | == Installing Node.js == | ||
| Line 40: | Line 70: | ||
$ npm -v | $ npm -v | ||
</source> | </source> | ||
| + | == Your First Node.js Application: Hello World == | ||
| − | + | Let’s build and run a very simple HTTP server using Node.js. | |
| − | Create a file named | + | Create a file named `hello.js` with the following contents: |
<source lang="javascript"> | <source lang="javascript"> | ||
| Line 49: | Line 80: | ||
http.createServer((req, res) => { | http.createServer((req, res) => { | ||
| − | + | res.writeHead(200, { "Content-Type": "text/plain" }); | |
| − | + | res.end("Hello World\n"); | |
}).listen(3456); | }).listen(3456); | ||
| Line 56: | Line 87: | ||
</source> | </source> | ||
| − | + | Now run the program from the command line: | |
<source lang="bash"> | <source lang="bash"> | ||
| Line 62: | Line 93: | ||
</source> | </source> | ||
| − | '''Important:''' | + | Open your browser and visit: |
| + | |||
| + | http://localhost:3456/ | ||
| + | |||
| + | You should see: | ||
| + | |||
| + | Hello World | ||
| + | |||
| + | To stop the server, press '''Ctrl+C''' in the terminal. | ||
| + | |||
| + | '''Note:''' If you are running this on your EC2 instance, you must open TCP port 3456 in your Security Group before it will be accessible from your browser. | ||
| + | |||
| + | --- | ||
| + | |||
| + | === Important: ES Modules vs require() === | ||
| + | |||
| + | You may notice that we used: | ||
| + | |||
| + | <source lang="javascript"> | ||
| + | import http from "node:http"; | ||
| + | </source> | ||
| + | |||
| + | Older Node.js examples often look like this instead: | ||
| + | |||
| + | <source lang="javascript"> | ||
| + | const http = require("http"); | ||
| + | </source> | ||
| + | |||
| + | These are two different module systems: | ||
| + | |||
| + | * '''CommonJS''' → uses <code>require()</code> (older Node style) | ||
| + | * '''ES Modules (ESM)''' → uses <code>import/export</code> (modern JavaScript standard) | ||
| + | |||
| + | In this course, we will use '''ES Modules'''. | ||
| + | |||
| + | To enable ES Modules in Node, create (or edit) a file named `package.json` in your project directory and include: | ||
<source lang="json"> | <source lang="json"> | ||
| Line 70: | Line 136: | ||
</source> | </source> | ||
| − | + | This tells Node to treat `.js` files as ES Modules. | |
| − | Install dependencies | + | If you do not include this line, Node may assume CommonJS and your `import` statements will not work. |
| + | |||
| + | Think of it this way: | ||
| + | |||
| + | * npm installs packages. | ||
| + | * ES Modules control how files import each other. | ||
| + | * `"type": "module"` tells Node which module system to use. | ||
| + | |||
| + | --- | ||
| + | |||
| + | === Walking Through the Hello World Code === | ||
| + | |||
| + | Let’s examine each line carefully. | ||
| + | |||
| + | * <code>import http from "node:http";</code> | ||
| + | This imports Node’s built-in HTTP module. Unlike PHP, Node does not automatically load server functionality for you. You must explicitly import the features you want to use. | ||
| + | |||
| + | * <code>http.createServer((req, res) => { ... }).listen(3456);</code> | ||
| + | This creates an HTTP server and tells it to listen on port 3456. | ||
| + | |||
| + | The function <code>(req, res) => { ... }</code> is a callback that runs every time a new HTTP request is received. | ||
| + | |||
| + | * <code>req</code> represents the incoming request (URL, headers, etc.) | ||
| + | * <code>res</code> represents the outgoing response you send back to the client | ||
| + | |||
| + | * <code>res.writeHead(200, { "Content-Type": "text/plain" });</code> | ||
| + | Sets the HTTP status code (200 = OK) and response headers. | ||
| + | |||
| + | * <code>res.end("Hello World\n");</code> | ||
| + | Sends the response body and finishes the request. | ||
| + | |||
| + | Even in this tiny example, you are already seeing non-blocking, event-driven behavior: | ||
| + | |||
| + | Whenever a connection is made, Node executes your callback function. Node is not looping over users or blocking while it waits. It reacts to events and runs your code when those events occur. | ||
| + | |||
| + | --- | ||
| + | == A Static File Server Using Node.js == | ||
| + | |||
| + | Node.js does not automatically serve static files the way Apache/PHP does. If a browser requests `/index.html`, nothing happens unless we explicitly write code to handle that request. | ||
| + | |||
| + | We have two options: | ||
| + | |||
| + | * Use a framework like Express (common in industry) | ||
| + | * Write our own simple static file server (excellent for learning how web servers work) | ||
| + | |||
| + | In this module, we will write our own. | ||
| + | |||
| + | --- | ||
| + | |||
| + | === Project Setup (Modern Node + ES Modules) === | ||
| + | |||
| + | Before writing any code, we must properly initialize our Node project. | ||
| + | |||
| + | Step 1: Create and enter a project directory | ||
| + | |||
| + | <source lang="bash"> | ||
| + | $ mkdir fileshare | ||
| + | $ cd fileshare | ||
| + | </source> | ||
| + | |||
| + | Step 2: Initialize the project | ||
| + | |||
| + | <source lang="bash"> | ||
| + | $ npm init -y | ||
| + | </source> | ||
| + | |||
| + | This creates a file named <code>package.json</code>. | ||
| + | |||
| + | Step 3: IMPORTANT — Enable ES Modules | ||
| + | |||
| + | Modern Node supports two module systems: | ||
| + | |||
| + | * CommonJS → uses <code>require()</code> | ||
| + | * ES Modules → uses <code>import/export</code> | ||
| + | |||
| + | All examples in this course use ES Modules. | ||
| + | |||
| + | After running <code>npm init -y</code>, your <code>package.json</code> may contain: | ||
| + | |||
| + | <source lang="json"> | ||
| + | "type": "commonjs" | ||
| + | </source> | ||
| + | |||
| + | You must change this to: | ||
| + | |||
| + | <source lang="json"> | ||
| + | "type": "module" | ||
| + | </source> | ||
| + | |||
| + | If the <code>"type"</code> field does not exist, add it manually. | ||
| + | |||
| + | A minimal correct <code>package.json</code> looks like: | ||
| + | |||
| + | <source lang="json"> | ||
| + | { | ||
| + | "name": "fileshare", | ||
| + | "version": "1.0.0", | ||
| + | "type": "module" | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | Without this change, Node will interpret your files using CommonJS, and <code>import</code> statements will fail. | ||
| + | |||
| + | Step 4: Install dependencies | ||
| + | |||
| + | We will use the <code>mime</code> package to determine correct Content-Type values. | ||
<source lang="bash"> | <source lang="bash"> | ||
| Line 78: | Line 249: | ||
</source> | </source> | ||
| − | npm automatically records | + | npm installs packages locally and automatically records them in <code>package.json</code>. |
| + | |||
| + | You should now see: | ||
| + | |||
| + | * <code>node_modules/</code> | ||
| + | * <code>package-lock.json</code> | ||
| + | * updated <code>package.json</code> | ||
| + | |||
| + | Your project is now correctly configured for modern Node.js. | ||
| + | |||
| + | --- | ||
| − | == Static File Server == | + | === Static File Server Code === |
| − | + | Create a file named <code>static_server.js</code>: | |
<source lang="javascript"> | <source lang="javascript"> | ||
import http from "node:http"; | import http from "node:http"; | ||
| + | import path from "node:path"; | ||
import { readFile } from "node:fs/promises"; | import { readFile } from "node:fs/promises"; | ||
import { existsSync } from "node:fs"; | import { existsSync } from "node:fs"; | ||
| − | |||
import mime from "mime"; | import mime from "mime"; | ||
import { fileURLToPath } from "node:url"; | import { fileURLToPath } from "node:url"; | ||
| + | // Recreate __dirname (not available automatically in ES Modules) | ||
const __filename = fileURLToPath(import.meta.url); | const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = path.dirname(__filename); | const __dirname = path.dirname(__filename); | ||
| − | const STATIC_DIR = " | + | // Directory containing files to serve |
| + | const STATIC_DIR = YOUR_STATIC_DIRECTORY_HERE | ||
| + | |||
| + | const server = http.createServer(async (req, res) => { | ||
| + | try { | ||
| + | // Extract requested path | ||
| + | const urlPath = new URL(req.url, "http://localhost").pathname; | ||
| + | |||
| + | // Build full filesystem path | ||
| + | const filename = path.join(__dirname, STATIC_DIR, decodeURIComponent(urlPath)); | ||
| + | |||
| + | // If file doesn't exist → 404 | ||
| + | if (!existsSync(filename)) { | ||
| + | res.writeHead(404, { "Content-Type": "text/plain" }); | ||
| + | res.end("Requested file not found"); | ||
| + | return; | ||
| + | } | ||
| − | + | // Read file (non-blocking) | |
| − | + | const data = await readFile(filename); | |
| − | |||
| − | |||
| − | + | // Determine Content-Type | |
| − | + | const mimetype = mime.getType(filename) || "application/octet-stream"; | |
| − | |||
| − | |||
| − | |||
| − | + | // Send file | |
| − | + | res.writeHead(200, { "Content-Type": mimetype }); | |
| + | res.end(data); | ||
| − | + | } catch (err) { | |
| − | + | res.writeHead(500, { "Content-Type": "text/plain" }); | |
| − | + | res.end("Internal server error"); | |
| − | + | } | |
| − | |||
| − | |||
}); | }); | ||
| − | + | server.listen(3456); | |
| + | console.log("Static server running at http://localhost:3456/"); | ||
</source> | </source> | ||
| − | + | --- | |
| − | == | + | === How This Server Works === |
| − | + | When a browser requests: | |
| − | + | http://localhost:3456/index.html | |
| − | + | The server: | |
| − | + | # Extracts the requested path from the URL | |
| − | + | # Builds the full file path inside the <code>public</code> directory | |
| − | + | # Checks whether the file exists | |
| − | + | # Reads the file using non-blocking I/O | |
| − | + | # Determines the correct Content-Type using <code>mime</code> | |
| + | # Sends the file back to the browser | ||
| − | + | Notice this line: | |
| − | = | + | <source lang="javascript"> |
| + | import { readFile } from "node:fs/promises"; | ||
| + | </source> | ||
| − | + | This allows us to use: | |
| − | <source lang=" | + | <source lang="javascript"> |
| − | + | const data = await readFile(filename); | |
</source> | </source> | ||
| − | + | This is modern async/await syntax. The file read is still non-blocking — Node can continue handling other requests while waiting for disk I/O. | |
| + | |||
| + | --- | ||
| + | |||
| + | === Security Note === | ||
| + | |||
| + | This server is intentionally simple for learning purposes. | ||
| + | |||
| + | A real production server must protect against: | ||
| + | |||
| + | * Directory traversal (paths like <code>../</code>) | ||
| + | * Arbitrary file access | ||
| + | * Improper input validation | ||
| + | |||
| + | We focus on understanding server mechanics first. | ||
| + | |||
| + | --- | ||
| + | |||
| + | === Running the Server === | ||
| + | |||
| + | Start the server: | ||
<source lang="bash"> | <source lang="bash"> | ||
| − | $ | + | $ node app.js |
</source> | </source> | ||
| − | package.json | + | Then open: |
| + | |||
| + | http://localhost:3456/ | ||
| + | |||
| + | If running on EC2, you must open port 3456 in your Security Group. | ||
| + | |||
| + | --- | ||
| + | |||
| + | === Common Setup Mistakes === | ||
| + | |||
| + | If your server fails to start or <code>import</code> fails: | ||
| + | |||
| + | * Check that <code>"type": "module"</code> is set in <code>package.json</code> | ||
| + | * Confirm Node version is modern (use <code>node -v</code>) | ||
| + | * Make sure port 3456 is open (EC2 users) | ||
| + | * Ensure your <code>public</code> directory exists and contains files | ||
| + | |||
| + | Most student issues come from missing the ES Modules step. | ||
| + | |||
| + | == Node.js Security == | ||
| + | |||
| + | Just because we're writing in JavaScript does not mean we can ignore the security principles you've been learning throughout CSE 330. | ||
| + | |||
| + | Node.js applications are still vulnerable to: | ||
| + | |||
| + | * Cross-Site Scripting (XSS) | ||
| + | * Cross-Site Request Forgery (CSRF) | ||
| + | * Injection attacks | ||
| + | * Directory traversal | ||
| + | * Insecure use of <code>eval()</code> | ||
| + | * Improper input validation | ||
| + | |||
| + | Node does not automatically protect you from these issues. The fact that you are running JavaScript on the server instead of PHP does not change the underlying security model of the web. | ||
| + | |||
| + | For example: | ||
| + | |||
| + | * If you echo unsanitized user input into HTML, you still create an XSS vulnerability. | ||
| + | * If you trust query parameters without validation, you still risk injection attacks. | ||
| + | * If your static server does not validate paths properly, you risk directory traversal attacks (e.g., requests containing <code>../</code>). | ||
| + | |||
| + | Modern Node frameworks (like Express) provide tools that can help mitigate certain risks, but they do not eliminate the need for careful programming. | ||
| − | + | Security remains the responsibility of the developer. | |
Latest revision as of 17:05, 25 February 2026
Node.js is a JavaScript runtime built on Chrome's V8 engine that allows JavaScript to run on the server. Frameworks such as Express run on top of Node.js.
Node.js is especially useful for real-time, high-concurrency applications due to its non-blocking, event-driven execution model.
Contents
Non-Blocking I/O
Node.js uses a non-blocking execution model. Instead of waiting for operations (like database queries or file reads) to complete, Node continues executing other work and runs a callback or Promise when the operation finishes.
Blocking example (conceptual):
var data = database.query("select * from news");
print(data[0].title);
The above pseudo-code queries a database, saves the result of the query in a variable named data, and then prints out the first title in data. The problem arises when multiple clients all want to access the server at the same time. In a blocking I/O model, a client must wait until all other clients finish before it can start its process. With intensive operations and thousands of clients, this can easily crash your server!
The solution is to use non-blocking I/O. Consider this revised pseudo-code:
Non-blocking example:
database.query("select * from news", function(data){
print(data[0].title);
});
The revised pseudo-code achieves the same end result as the first version. However, it allows other clients to perform tasks while this process is waiting for the database to be queried! The code occurs in two parts: first, we start the query. Second, when the query is complete, our anonymous callback function is executed. In Node.js, callback functions are usually passed one or more parameters (often an error parameter and a result parameter).
Better yet, with non-blocking I/O, you avoid many of the concurrency headaches common in multi-threaded server environments, because JavaScript code in Node runs in a single main thread. Nifty!
Important clarification: "Single-threaded" does not mean "no concurrency." Node can handle many concurrent connections. The difference is that it avoids running multiple threads of JavaScript at the same time, which reduces race conditions and locking complexity. The concurrency happens in the underlying event loop and operating system, not by spawning a new JavaScript thread per request.
Modern Syntax: Promises and async/await
Historically, Node relied heavily on callback functions, like the example above. While this model still works, large programs with many nested callbacks can become difficult to read and maintain (sometimes called "callback hell").
Modern Node commonly uses Promises and the async/await syntax built on top of them. Conceptually, the behavior is identical: the operation begins, Node continues handling other work, and when the operation finishes, your code resumes.
For example:
async function getTitle() {
const data = await database.query("select * from news");
console.log(data[0].title);
}
Behind the scenes, this is still non-blocking I/O. The `await` keyword does not freeze the entire server; it simply pauses that particular function until the Promise resolves, while the event loop continues processing other incoming requests.
So:
- Callbacks explain how Node originally structured asynchronous work.
- Promises formalize asynchronous behavior.
- async/await makes asynchronous code look more like synchronous code — but it remains non-blocking underneath.
Understanding the callback model helps you understand how Node works internally. Understanding async/await helps you write modern, readable Node applications.
Installing Node.js
We use nvm (Node Version Manager) to install Node:
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
$ source ~/.bashrc
$ nvm install --lts
$ node -v
$ npm -v
Your First Node.js Application: Hello World
Let’s build and run a very simple HTTP server using Node.js.
Create a file named `hello.js` with the following contents:
import http from "node:http";
http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello World\n");
}).listen(3456);
console.log("Server running at http://localhost:3456/");
Now run the program from the command line:
$ node hello.js
Open your browser and visit:
You should see:
Hello World
To stop the server, press Ctrl+C in the terminal.
Note: If you are running this on your EC2 instance, you must open TCP port 3456 in your Security Group before it will be accessible from your browser.
---
Important: ES Modules vs require()
You may notice that we used:
import http from "node:http";
Older Node.js examples often look like this instead:
const http = require("http");
These are two different module systems:
- CommonJS → uses
require()(older Node style) - ES Modules (ESM) → uses
import/export(modern JavaScript standard)
In this course, we will use ES Modules.
To enable ES Modules in Node, create (or edit) a file named `package.json` in your project directory and include:
{
"type": "module"
}
This tells Node to treat `.js` files as ES Modules.
If you do not include this line, Node may assume CommonJS and your `import` statements will not work.
Think of it this way:
- npm installs packages.
- ES Modules control how files import each other.
- `"type": "module"` tells Node which module system to use.
---
Walking Through the Hello World Code
Let’s examine each line carefully.
import http from "node:http";
This imports Node’s built-in HTTP module. Unlike PHP, Node does not automatically load server functionality for you. You must explicitly import the features you want to use.
http.createServer((req, res) => { ... }).listen(3456);
This creates an HTTP server and tells it to listen on port 3456.
The function (req, res) => { ... } is a callback that runs every time a new HTTP request is received.
*reqrepresents the incoming request (URL, headers, etc.) *resrepresents the outgoing response you send back to the client
res.writeHead(200, { "Content-Type": "text/plain" });
Sets the HTTP status code (200 = OK) and response headers.
res.end("Hello World\n");
Sends the response body and finishes the request.
Even in this tiny example, you are already seeing non-blocking, event-driven behavior:
Whenever a connection is made, Node executes your callback function. Node is not looping over users or blocking while it waits. It reacts to events and runs your code when those events occur.
---
A Static File Server Using Node.js
Node.js does not automatically serve static files the way Apache/PHP does. If a browser requests `/index.html`, nothing happens unless we explicitly write code to handle that request.
We have two options:
- Use a framework like Express (common in industry)
- Write our own simple static file server (excellent for learning how web servers work)
In this module, we will write our own.
---
Project Setup (Modern Node + ES Modules)
Before writing any code, we must properly initialize our Node project.
Step 1: Create and enter a project directory
$ mkdir fileshare
$ cd fileshare
Step 2: Initialize the project
$ npm init -y
This creates a file named package.json.
Step 3: IMPORTANT — Enable ES Modules
Modern Node supports two module systems:
- CommonJS → uses
require() - ES Modules → uses
import/export
All examples in this course use ES Modules.
After running npm init -y, your package.json may contain:
"type": "commonjs"
You must change this to:
"type": "module"
If the "type" field does not exist, add it manually.
A minimal correct package.json looks like:
{
"name": "fileshare",
"version": "1.0.0",
"type": "module"
}
Without this change, Node will interpret your files using CommonJS, and import statements will fail.
Step 4: Install dependencies
We will use the mime package to determine correct Content-Type values.
$ npm install mime
npm installs packages locally and automatically records them in package.json.
You should now see:
node_modules/package-lock.json- updated
package.json
Your project is now correctly configured for modern Node.js.
---
Static File Server Code
Create a file named static_server.js:
import http from "node:http";
import path from "node:path";
import { readFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import mime from "mime";
import { fileURLToPath } from "node:url";
// Recreate __dirname (not available automatically in ES Modules)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Directory containing files to serve
const STATIC_DIR = YOUR_STATIC_DIRECTORY_HERE
const server = http.createServer(async (req, res) => {
try {
// Extract requested path
const urlPath = new URL(req.url, "http://localhost").pathname;
// Build full filesystem path
const filename = path.join(__dirname, STATIC_DIR, decodeURIComponent(urlPath));
// If file doesn't exist → 404
if (!existsSync(filename)) {
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Requested file not found");
return;
}
// Read file (non-blocking)
const data = await readFile(filename);
// Determine Content-Type
const mimetype = mime.getType(filename) || "application/octet-stream";
// Send file
res.writeHead(200, { "Content-Type": mimetype });
res.end(data);
} catch (err) {
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Internal server error");
}
});
server.listen(3456);
console.log("Static server running at http://localhost:3456/");
---
How This Server Works
When a browser requests:
http://localhost:3456/index.html
The server:
- Extracts the requested path from the URL
- Builds the full file path inside the
publicdirectory - Checks whether the file exists
- Reads the file using non-blocking I/O
- Determines the correct Content-Type using
mime - Sends the file back to the browser
Notice this line:
import { readFile } from "node:fs/promises";
This allows us to use:
const data = await readFile(filename);
This is modern async/await syntax. The file read is still non-blocking — Node can continue handling other requests while waiting for disk I/O.
---
Security Note
This server is intentionally simple for learning purposes.
A real production server must protect against:
- Directory traversal (paths like
../) - Arbitrary file access
- Improper input validation
We focus on understanding server mechanics first.
---
Running the Server
Start the server:
$ node app.js
Then open:
If running on EC2, you must open port 3456 in your Security Group.
---
Common Setup Mistakes
If your server fails to start or import fails:
- Check that
"type": "module"is set inpackage.json - Confirm Node version is modern (use
node -v) - Make sure port 3456 is open (EC2 users)
- Ensure your
publicdirectory exists and contains files
Most student issues come from missing the ES Modules step.
Node.js Security
Just because we're writing in JavaScript does not mean we can ignore the security principles you've been learning throughout CSE 330.
Node.js applications are still vulnerable to:
- Cross-Site Scripting (XSS)
- Cross-Site Request Forgery (CSRF)
- Injection attacks
- Directory traversal
- Insecure use of
eval() - Improper input validation
Node does not automatically protect you from these issues. The fact that you are running JavaScript on the server instead of PHP does not change the underlying security model of the web.
For example:
- If you echo unsanitized user input into HTML, you still create an XSS vulnerability.
- If you trust query parameters without validation, you still risk injection attacks.
- If your static server does not validate paths properly, you risk directory traversal attacks (e.g., requests containing
../).
Modern Node frameworks (like Express) provide tools that can help mitigate certain risks, but they do not eliminate the need for careful programming.
Security remains the responsibility of the developer.