Unit 2 - Notes
INT222
Unit 2: Implementing HTTP Services & Basic Websites With Node.JS
1. Introduction to the HTTP Module
Node.js is built with a core module named http which allows Node.js to transfer data over the Hyper Text Transfer Protocol (HTTP). This module is capable of creating an HTTP server that listens to server ports and gives a response back to the client.
Key Characteristics
- Built-in: It does not require installation via NPM; it is part of the Node.js core.
- Event-Driven: It inherits from the
EventEmitterclass. - Low-level: It provides low-level access to HTTP requests and responses, requiring manual handling of data chunks, headers, and routing logic compared to frameworks like Express.
To include the HTTP module:
const http = require('http');
2. Setting up a Basic HTTP Server
The http.createServer() method includes a callback function that is executed every time a request is received. This callback receives two arguments: the Request object and the Response object.
Syntax
const server = http.createServer((req, res) => {
// Logic to handle request and send response
});
server.listen(port, hostname, callback);
Example: "Hello World" Server
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
res.write('Hello World from Node.js Server');
res.end();
});
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});
3. Understanding Request and Response Objects
The Request Object (req)
The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on. It is an instance of http.IncomingMessage.
Key Properties:
req.url: The URL string (the path after the domain).req.method: The HTTP method used (GET, POST, DELETE, etc.).req.headers: An object containing the request headers.
The Response Object (res)
The res object represents the HTTP response that an Express app sends when it gets an HTTP request. It is an instance of http.ServerResponse.
Key Methods:
res.write(chunk): Sends a chunk of the response body. Can be called multiple times.res.end([data]): Signals to the server that all of the response headers and body have been sent. Must be called on every response.res.setHeader(name, value): Sets a single header value.
4. Implementing Basic Routing (Without Frameworks)
In raw Node.js, routing must be handled manually by inspecting the req.url and req.method within the server callback.
Example: Switch-Case Routing
const http = require('http');
const server = http.createServer((req, res) => {
const url = req.url;
const method = req.method;
if (url === '/') {
res.write('<html><body><h1>Home Page</h1></body></html>');
res.end();
} else if (url === '/about' && method === 'GET') {
res.write('<html><body><h1>About Us</h1></body></html>');
res.end();
} else if (url === '/api/data') {
res.setHeader('Content-Type', 'application/json');
res.write(JSON.stringify({ message: "Here is your data" }));
res.end();
} else {
res.statusCode = 404;
res.write('<html><body><h1>404 Not Found</h1></body></html>');
res.end();
}
});
server.listen(3000);
5. Setting Response Headers and Status Codes
Properly setting headers and status codes is crucial for clients (browsers/APIs) to understand how to process the response.
Status Codes (res.statusCode)
- 200: OK (Standard response for successful HTTP requests).
- 201: Created (Request fulfilled, new resource created).
- 301: Moved Permanently.
- 400: Bad Request (Client error).
- 401: Unauthorized.
- 404: Not Found.
- 500: Internal Server Error.
Response Headers (res.writeHead or res.setHeader)
Headers provide metadata about the response, such as the content type.
Using writeHead (Sets status and headers simultaneously):
res.writeHead(200, {
'Content-Type': 'application/json',
'X-Powered-By': 'Node.js'
});
Common Content-Types:
text/html: HTML documents.text/plain: Plain text.application/json: JSON data (standard for APIs).
6. Introducing Express.js
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
Why use Express over raw Node.js?
- Simplified Routing: Clean syntax for handling different HTTP verbs and URL paths.
- Middleware: Easy integration of functions that have access to the request object, response object, and the next middleware function (e.g., authentication, logging).
- Template Engines: Easy integration with engines like EJS, Pug, or Handlebars.
- Static Files: Built-in middleware to serve static assets (CSS, images).
7. Installing Express
To use Express, it must be installed via the Node Package Manager (NPM) into a project initialized with a package.json.
Step 1: Initialize project
mkdir my-express-app
cd my-express-app
npm init -y
Step 2: Install Express
npm install express --save
8. Express Basics: GET and POST
Express simplifies request handling by providing methods corresponding to HTTP verbs.
Setup
const express = require('express');
const app = express();
const PORT = 3000;
GET Request
Used to retrieve data.
app.get('/', (req, res) => {
res.send('Welcome to the Home Page'); // .send() automatically sets Content-Type and ends response
});
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'John' }]); // .json() sends JSON response
});
POST Request
Used to submit data to be processed to a specified resource.
app.post('/users', (req, res) => {
// Logic to add a user would go here
res.status(201).send('User Created');
});
Starting the Server
app.listen(PORT, () => {
console.log(`Express server running on port ${PORT}`);
});
9. Body-Parser (Handling Request Body)
To read data sent in a POST request (e.g., from a form or an API client), the server must parse the body of the request.
- Historically:
body-parserwas a separate npm package. - Currently (Express 4.16.0+): It is built into Express.
Configuration
You must configure the middleware before your route definitions.
// To parse JSON payloads (e.g., from React/Vue/Postman)
app.use(express.json());
// To parse URL-encoded data (e.g., standard HTML forms)
app.use(express.urlencoded({ extended: true }));
Accessing Data
Once parsed, the data is available in req.body.
app.post('/login', (req, res) => {
const username = req.body.username;
const password = req.body.password;
console.log(`Attempting login for: ${username}`);
res.send('Login received');
});
10. Modular Routing with express.Router
As an application grows, putting all routes in the main app.js file becomes unmanageable. express.Router creates modular, mountable route handlers (often called "mini-apps").
Creating a Router Module (routes/users.js)
const express = require('express');
const router = express.Router();
// Define routes relative to the mount path
router.get('/', (req, res) => {
res.send('Get all users');
});
router.get('/:id', (req, res) => {
res.send(`Get user with ID: ${req.params.id}`);
});
module.exports = router;
Mounting the Router (app.js)
const userRoutes = require('./routes/users');
// All routes in userRoutes will be prefixed with /users
// e.g., /users/ and /users/:id
app.use('/users', userRoutes);
11. Input Validation with express-validator
express-validator is a set of Express.js middlewares that wraps validator.js. It allows you to validate and sanitize inputs (body, query, params) to ensure data integrity and security.
Installation
npm install express-validator
Implementation Workflow
- Import
checkorbodyandvalidationResult. - Pass validation chains as middleware to the route.
- Check for errors inside the route handler.
Example Code
const { body, validationResult } = require('express-validator');
app.post('/register', [
// 1. Define Rules
body('email').isEmail().withMessage('Must be a valid email'),
body('password').isLength({ min: 6 }).withMessage('Password must be 6+ chars'),
body('age').optional().isInt({ min: 18 }).withMessage('Must be 18 or older')
], (req, res) => {
// 2. Check for Validation Errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
// Return 400 Bad Request with the error array
return res.status(400).json({ errors: errors.array() });
}
// 3. Proceed if valid
const { email, password } = req.body;
res.send(`User registered with email: ${email}`);
});
Common Validators
isEmail(): Checks for valid email format.isLength({ min, max }): Checks string length.trim(): Sanitizer that removes whitespace.escape(): Sanitizer that replaces HTML characters (prevents XSS).normalizeEmail(): Sanitizer that canonicalizes the email address.