📘 Express.
js Learning Guide
📑 Contents
1. ⚙️Project Setup
2. Basic Server & Responses
3. 🧩 Routing: Params & Query
4. 🧱 Middleware & Static Files
5. 🌐 CORS
6. 💾 SQLite: Setup & Queries
7. 🔐 Auth: Register / Login / Session
8. 🛒 Cart Endpoints
9. Protecting Routes
10. 🌍 HTTP Status Codes
11. 🧠 Best Practices
🎯 Goal & Scope
This mini-book teaches [Link] from setup to a small authenticated API with a shopping
cart, using SQLite for persistence. No exercises—pure explanation and code you can paste
and run.
⚙️Project Setup
Initialize a Node project and install Express. Use ES Modules for modern import/export.
npm init -y
npm install express
// In [Link] add:
"type": "module"
🟢 Tip — Keep secrets out of source code. Use environment variables.
🟢 Tip — Dependencies are tracked in "dependencies" inside [Link]. Avoid editing
node_modules or [Link].
Create a Basic Server
An Express app is a function we configure with middleware and routes, and then bind to a
port.
import express from 'express';
const app = express();
const PORT = 3000;
[Link](PORT, () => [Link](`Listening on ${PORT}`));
🟠 Note — Use [Link] in production platforms.
📤 Sending Responses
Use [Link] for JSON APIs and [Link] for text/HTML. Always terminate a request with a
response or next().
[Link]('/', (req, res) => {
[Link]({ ok: true });
});
📥 Understanding the Request Object
[Link] holds parsed request payloads (after [Link]()), [Link] holds path
params, and [Link] holds URL query strings.
[Link]([Link]());
[Link]('/search', (req, res) => {
const { q, page = 1 } = [Link];
[Link]({ q, page });
});
🧩 Routing: Params, Query, and Routers
Routers encapsulate related endpoints, improving organization and testability.
// routes/[Link]
import express from 'express';
import { productsController } from '../controllers/[Link]';
const router = [Link]();
[Link]('/products', productsController);
export default router;
// [Link]
import apiRouter from './routes/[Link]';
[Link]('/api', apiRouter);
// 404 fallback
[Link]((req, res) => [Link](404).json({ message: 'Not Found' }));
🧱 Middleware
Middleware runs in sequence. It can read/modify req and res, then call next() to continue.
Common uses: logging, parsing, security, auth.
[Link]((req, res, next) => {
[Link](`${[Link]} ${[Link]}`);
next();
});
// Built-in helpers
[Link]([Link]());
[Link]([Link]('public'));
🌐 CORS
Enable cross-origin requests when your frontend is hosted on another origin.
import cors from 'cors';
[Link](cors({
origin: '[Link]
methods: ['GET','POST','PUT','DELETE'],
}));
💾 SQLite Setup
SQLite is a serverless SQL database stored in a single file. We use the sqlite and sqlite3
packages together with async/await.
npm install sqlite3 sqlite
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import path from 'node:path';
export async function getDBConnection() {
return open({ filename: [Link]('[Link]'), driver: [Link] });
}
export async function migrate() {
const db = await getDBConnection();
await [Link](`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
email TEXT UNIQUE,
password TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT, artist TEXT, genre TEXT, price REAL
);
CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
product_id INTEGER,
quantity INTEGER DEFAULT 1,
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(product_id) REFERENCES products(id)
);
`);
await [Link]();
}
🔎 Dynamic Queries: Filtering & Search
Compose SQL based on user-supplied query params. Always bind values to placeholders (?)
to avoid SQL injection.
export async function listProducts(req, res) {
const db = await getDBConnection();
let query = 'SELECT * FROM products';
const params = [];
const { genre, search } = [Link];
if (genre) {
query += ' WHERE genre = ?';
[Link](genre);
} else if (search) {
query += ' WHERE title LIKE ? OR artist LIKE ? OR genre LIKE ?';
const s = `%${search}%`;
[Link](s, s, s);
}
const rows = await [Link](query, params);
[Link](rows);
}
🟢 Tip — Use parameterized queries (?) to prevent SQL injection.
🔐 Authentication with Sessions
We use express-session to persist a user ID across requests after login. Passwords are
hashed with bcryptjs.
npm install express-session validator bcryptjs
import session from 'express-session';
[Link](session({
secret: 'secret',
resave: false,
saveUninitialized: false
}));
import validator from 'validator';
import bcrypt from 'bcryptjs';
import { getDBConnection } from './[Link]';
export async function registerUser(req, res) {
const { username, email, password } = [Link];
if (!username || !email || !password) return [Link](400).json({ error: 'All fields required' });
if () return [Link](400).json({ error: 'Invalid email' });
const hash = await [Link](password, 10);
const db = await getDBConnection();
await [Link]('INSERT INTO users (username,email,password) VALUES (?,?,?)', [[Link](),
[Link](), hash]);
[Link](201).json({ message: 'User registered' });
}
export async function loginUser(req, res) {
const { username, password } = [Link];
const db = await getDBConnection();
const user = await [Link]('SELECT * FROM users WHERE username = ?', [[Link]()]);
if (!user) return [Link](401).json({ error: 'Invalid credentials' });
const ok = await [Link](password, [Link]);
if (!ok) return [Link](401).json({ error: 'Invalid credentials' });
[Link] = [Link];
[Link]({ message: 'Logged in' });
}
export function logout(req, res) {
[Link](() => [Link]({ message: 'Logged out' }));
}
Protecting Routes
function requireAuth(req, res, next) {
if (![Link]) return [Link](403).json({ error: 'Unauthorized' });
next();
}
[Link]('/api/cart', requireAuth);
🛒 Shopping Cart Endpoints
The cart is per-user and stores product references and quantities.
export async function getAll(req, res) {
const db = await getDBConnection();
const items = await [Link](`
SELECT [Link] AS cartItemId, [Link], [Link], [Link], [Link]
FROM cart_items ci
JOIN products p ON [Link] = ci.product_id
WHERE ci.user_id = ?
`, [[Link]]);
[Link]({ items });
}
export async function addToCart(req, res) {
const db = await getDBConnection();
const productId = parseInt([Link], 10);
if (isNaN(productId)) return [Link](400).json({ error: 'Invalid product ID' });
const userId = [Link];
const existing = await [Link]('SELECT * FROM cart_items WHERE user_id = ? AND product_id = ?',
[userId, productId]);
if (existing) {
await [Link]('UPDATE cart_items SET quantity = quantity + 1 WHERE id = ?', [[Link]]);
} else {
await [Link]('INSERT INTO cart_items (user_id, product_id, quantity) VALUES (?, ?, 1)', [userId,
productId]);
}
[Link]({ message: 'Added to cart' });
}
export async function deleteAll(req, res) {
const db = await getDBConnection();
await [Link]('DELETE FROM cart_items WHERE user_id = ?', [[Link]]);
[Link](204).send();
}
export async function deleteItem(req, res) {
const db = await getDBConnection();
const itemId = parseInt([Link], 10);
if (isNaN(itemId)) return [Link](400).json({ error: 'Invalid item ID' });
const item = await [Link]('SELECT quantity FROM cart_items WHERE id = ? AND user_id = ?',
[itemId, [Link]]);
if (!item) return [Link](400).json({ error: 'Item not found' });
await [Link]('DELETE FROM cart_items WHERE id = ? AND user_id = ?', [itemId,
[Link]]);
[Link](204).send();
}
export async function getCartCount(req, res) {
const db = await getDBConnection();
const result = await [Link]('SELECT SUM(quantity) AS totalItems FROM cart_items WHERE user_id
= ?', [[Link]]);
[Link]({ totalItems: [Link] || 0 });
}
🧰 Error Handling & Status Codes
Use centralized error handlers. Always send appropriate HTTP status codes.
// [Link]((req, res) => [Link](404).json({ message: 'Not Found' }));
// Error [Link]((err, req, res, next) => {
[Link](err);
[Link](500).json({ error: 'Internal Server Error' });
});
Status overview: 2xx Success • 4xx Client Error • 5xx Server Error. Prefer 201 for resource
creation and 204 for no-content deletes.
✅ Wrap-up
You now have a minimal but complete pattern: Express app → routers → middleware →
SQLite → session auth → protected cart endpoints. Expand by adding validation layers and
structured logging.