HTML & JS Topic

Node.js Express.js Fundamentals

What is Express.js?

Express.js is a minimal, flexible, and unopinionated web application framework for Node.js. It's the most popular Node.js framework that provides a robust set of features for building web applications and APIs.

Key Characteristics:

  • Minimal and unopinionated: Doesn't impose strict patterns

  • Middleware-based: Uses middleware functions for request processing

  • Routing: Powerful routing system

  • HTTP utilities: Methods and middleware for HTTP requests

  • View system: Template engine integration

  • Fast: Built on top of Node.js http module

Why Express.js over plain http module?

javascript

// Plain Node.js http module
const http = require('http');
const server = http.createServer((req, res) => {
  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Home Page');
  } else if (req.url === '/api/users' && req.method === 'GET') {
    // Complex routing logic needed...
  }
});

// Express.js (much cleaner)
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Home Page');
});

app.get('/api/users', (req, res) => {
  // Clean routing
});

Installation:

bash

# Create new project
mkdir express-app
cd express-app
npm init -y

# Install Express
npm install express

# For development (optional)
npm install -D nodemon

Creating an Express Server

Basic Express Server:

javascript

// app.js
const express = require('express');

// Create Express application
const app = express();
const PORT = process.env.PORT || 3000;

// Define routes
app.get('/', (req, res) => {
  res.send('Hello Express!');
});

// Start server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

Advanced Server Setup:

javascript

// server.js
const express = require('express');
const path = require('path');

const app = express();

// Configuration
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs'); // If using template engine
app.set('views', path.join(__dirname, 'views'));

// Parse JSON and URL-encoded bodies
app.use(express.json()); // For parsing application/json
app.use(express.urlencoded({ extended: true })); // For parsing application/x-www-form-urlencoded

// Static files middleware
app.use(express.static(path.join(__dirname, 'public')));

// Custom middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Pass control to next middleware
});

// Routes
app.get('/', (req, res) => {
  res.send('Welcome to Express Server');
});

// Error handling middleware (should be last)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

// 404 handler (catch all)
app.use((req, res) => {
  res.status(404).send('Page not found');
});

// Start server with error handling
const server = app.listen(app.get('port'), () => {
  console.log(`Server listening on port ${app.get('port')}`);
});

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM signal received: closing HTTP server');
  server.close(() => {
    console.log('HTTP server closed');
  });
});

Package.json Scripts:

json

{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "test": "jest"
  }
}

Routing (GET, POST, PUT, DELETE)

Basic Routing Methods:

javascript

const express = require('express');
const app = express();

// GET method - Retrieve data
app.get('/users', (req, res) => {
  res.send('GET: List all users');
});

app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`GET: User with ID ${userId}`);
});

// POST method - Create data
app.post('/users', (req, res) => {
  res.send('POST: Create new user');
});

// PUT method - Update entire resource
app.put('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`PUT: Update user ${userId}`);
});

// PATCH method - Partial update
app.patch('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`PATCH: Partially update user ${userId}`);
});

// DELETE method - Delete data
app.delete('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.send(`DELETE: Delete user ${userId}`);
});

// All HTTP methods
app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section...');
  next(); // Pass control to the next handler
});

// Chained route handlers
app.route('/book')
  .get((req, res) => {
    res.send('Get a random book');
  })
  .post((req, res) => {
    res.send('Add a book');
  })
  .put((req, res) => {
    res.send('Update the book');
  });

Route Parameters:

javascript

// Single parameter
app.get('/users/:userId', (req, res) => {
  res.send(`User ID: ${req.params.userId}`);
});

// Multiple parameters
app.get('/users/:userId/books/:bookId', (req, res) => {
  res.send(`User ${req.params.userId}, Book ${req.params.bookId}`);
});

// Optional parameters
app.get('/flights/:from?/:to?', (req, res) => {
  const from = req.params.from || 'JFK';
  const to = req.params.to || 'LAX';
  res.send(`Searching flights from ${from} to ${to}`);
});

// Regex in route parameters
app.get('/user/:id(\\d+)', (req, res) => {
  // Only matches if id is digits
  res.send(`User ID (digits only): ${req.params.id}`);
});

app.get('/file/:name.:ext', (req, res) => {
  // Matches /file/photo.jpg
  res.send(`File: ${req.params.name}.${req.params.ext}`);
});

Query Strings:

javascript

// GET /search?q=express&sort=desc
app.get('/search', (req, res) => {
  const query = req.query.q;
  const sort = req.query.sort || 'asc';
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  
  res.json({
    message: 'Search results',
    query,
    sort,
    page,
    limit,
    results: []
  });
});

Route Handlers (Multiple Callbacks):

javascript

// Multiple callback functions
app.get('/example', 
  (req, res, next) => {
    console.log('First callback');
    next(); // Pass to next callback
  },
  (req, res, next) => {
    console.log('Second callback');
    next();
  },
  (req, res) => {
    console.log('Third callback');
    res.send('Response sent');
  }
);

// Array of callback functions
const cb1 = (req, res, next) => {
  console.log('Callback 1');
  next();
};

const cb2 = (req, res, next) => {
  console.log('Callback 2');
  next();
};

const cb3 = (req, res) => {
  res.send('Response from callback chain');
};

app.get('/chain', [cb1, cb2, cb3]);

Router Module (Modular Routes):

javascript

// routes/users.js
const express = require('express');
const router = express.Router();

// Middleware specific to this router
router.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

// Define routes
router.get('/', (req, res) => {
  res.send('Users home page');
});

router.get('/:id', (req, res) => {
  res.send(`User ${req.params.id}`);
});

router.post('/', (req, res) => {
  res.send('Create user');
});

module.exports = router;

// In main app.js
const usersRouter = require('./routes/users');
app.use('/users', usersRouter); // All routes in usersRouter are prefixed with /users

Request & Response Objects

Request Object (req):

Contains information about the HTTP request.

javascript

app.use((req, res, next) => {
  // Request properties
  console.log('Request Properties:');
  console.log('Method:', req.method);           // GET, POST, etc.
  console.log('URL:', req.url);                 // /path?query=string
  console.log('Path:', req.path);               // /path
  console.log('Hostname:', req.hostname);       // localhost
  console.log('IP:', req.ip);                   // ::1
  console.log('Protocol:', req.protocol);       // http
  console.log('Secure:', req.secure);           // false (true for HTTPS)
  console.log('Query:', req.query);             // Parsed query string object
  console.log('Params:', req.params);           // Route parameters
  console.log('Headers:', req.headers);         // Request headers
  console.log('Cookies:', req.cookies);         // Requires cookie-parser middleware
  console.log('Body:', req.body);               // Requires body-parser middleware
  console.log('Files:', req.files);             // Requires multer middleware
  
  // Request methods
  console.log('Is AJAX?', req.xhr);             // true for XMLHttpRequest
  console.log('Accepts JSON?', req.accepts('json'));
  console.log('Accepts HTML?', req.accepts('html'));
  
  next();
});

Accessing Request Data:

javascript

// Route with multiple ways to access data
app.post('/api/users/:id', (req, res) => {
  // Route parameters
  const userId = req.params.id;
  
  // Query string
  const debug = req.query.debug === 'true';
  
  // Request body (JSON or form data)
  const { name, email, age } = req.body;
  
  // Headers
  const contentType = req.get('Content-Type');
  const authorization = req.header('Authorization');
  
  // Check content type
  if (req.is('json')) {
    console.log('Request is JSON');
  }
  
  res.json({
    userId,
    name,
    email,
    age,
    debug,
    contentType
  });
});

Response Object (res):

Used to send response to client.

javascript

// Sending different types of responses
app.get('/responses', (req, res) => {
  // Send plain text
  // res.send('Hello World');
  
  // Send JSON
  // res.json({ message: 'Success', data: { id: 1 } });
  
  // Send file
  // res.sendFile('/path/to/file.pdf');
  
  // Send status code only
  // res.sendStatus(204);
  
  // Set status and send
  // res.status(201).send('Resource created');
  
  // Set headers
  res.set('Content-Type', 'text/plain');
  res.set('X-Custom-Header', 'CustomValue');
  
  // Set multiple headers
  res.set({
    'Content-Type': 'application/json',
    'X-Powered-By': 'Express'
  });
  
  // Set cookie (requires cookie-parser)
  // res.cookie('username', 'john', { maxAge: 900000, httpOnly: true });
  
  // Clear cookie
  // res.clearCookie('username');
  
  // Redirect
  // res.redirect('/new-location');
  // res.redirect(301, '/permanent-location');
  // res.redirect('https://google.com');
  
  // Render template (with view engine)
  // res.render('index', { title: 'Home', users: [] });
  
  // End response (no data)
  // res.end();
  
  // Send response
  res.send('Response sent');
});

// Chaining response methods
app.get('/chain', (req, res) => {
  res
    .status(200)
    .set('X-Custom', 'Value')
    .cookie('token', 'abc123', { httpOnly: true })
    .json({ success: true, message: 'Chained response' });
});

Response Methods in Detail:

javascript

// 1. res.send() - Auto-detects content type
app.get('/send', (req, res) => {
  res.send('String');                    // text/html
  res.send('<p>HTML</p>');               // text/html
  res.send({ message: 'JSON' });         // application/json
  res.send(Buffer.from('buffer'));       // application/octet-stream
});

// 2. res.json() - Always sends JSON
app.get('/json', (req, res) => {
  res.json({ success: true, data: null });
  // Equivalent to: res.send({ success: true })
});

// 3. res.jsonp() - JSON with padding (for JSONP)
app.get('/jsonp', (req, res) => {
  res.jsonp({ user: 'tobi' });
  // If ?callback=cb, returns: cb({ "user": "tobi" })
});

// 4. res.download() - Prompt file download
app.get('/download', (req, res) => {
  res.download('/path/to/file.pdf');
  res.download('/path/to/file.pdf', 'custom-name.pdf');
  res.download('/path/to/file.pdf', 'custom-name.pdf', (err) => {
    if (err) {
      // Handle error
    }
  });
});

// 5. res.sendFile() - Send file
app.get('/file', (req, res) => {
  const options = {
    root: path.join(__dirname, 'public'),
    dotfiles: 'deny',
    headers: {
      'x-timestamp': Date.now(),
      'x-sent': true
    }
  };
  
  res.sendFile('image.jpg', options, (err) => {
    if (err) {
      next(err);
    }
  });
});

// 6. res.render() - Render view template
app.get('/view', (req, res) => {
  res.render('index', {
    title: 'Home Page',
    users: [{ name: 'John' }, { name: 'Jane' }]
  });
});

Middleware Concept

What is Middleware?

Middleware functions are functions that have access to:

  1. Request object (req)

  2. Response object (res)

  3. Next middleware function (next)

javascript

// Middleware function signature
function middleware(req, res, next) {
  // Do something with req/res
  // Call next() to pass to next middleware
  // Or send response to end chain
  next();
}

Types of Middleware:

javascript

// 1. Application-level middleware
app.use((req, res, next) => {
  console.log('Application middleware - runs for ALL routes');
  next();
});

// 2. Router-level middleware
const router = express.Router();
router.use((req, res, next) => {
  console.log('Router middleware');
  next();
});

// 3. Built-in middleware
app.use(express.json());      // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
app.use(express.static('public')); // Serve static files

// 4. Third-party middleware
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');

app.use(cors());
app.use(helmet());
app.use(morgan('combined'));

// 5. Error-handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

Creating Custom Middleware:

javascript

// Logger middleware
const logger = (req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
};

// Authentication middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  // Verify token (simplified)
  if (token !== 'secret-token') {
    return res.status(403).json({ error: 'Invalid token' });
  }
  
  // Attach user to request
  req.user = { id: 1, name: 'John Doe' };
  next();
};

// Validation middleware
const validateUser = (req, res, next) => {
  const { name, email, password } = req.body;
  
  if (!name || !email || !password) {
    return res.status(400).json({ 
      error: 'Name, email, and password are required' 
    });
  }
  
  if (password.length < 6) {
    return res.status(400).json({
      error: 'Password must be at least 6 characters'
    });
  }
  
  next();
};

// Using middleware
app.use(logger); // Applies to all routes

app.get('/public', (req, res) => {
  res.send('Public route');
});

app.get('/private', authenticate, (req, res) => {
  res.json({ message: 'Private route', user: req.user });
});

app.post('/register', validateUser, (req, res) => {
  // If we get here, validation passed
  res.status(201).json({ message: 'User registered successfully' });
});

Middleware Execution Order:

javascript

// Order matters in middleware!
app.use((req, res, next) => {
  console.log('First middleware');
  req.customData = 'Data from first middleware';
  next();
});

app.use((req, res, next) => {
  console.log('Second middleware');
  console.log('Custom data:', req.customData);
  next();
});

app.use('/admin', (req, res, next) => {
  console.log('Admin middleware - only for /admin routes');
  next();
});

app.get('/', (req, res) => {
  res.send('Home - all middleware ran');
});

app.get('/admin', (req, res) => {
  res.send('Admin page - admin middleware ran');
});

Error Handling Middleware:

javascript

// Must have 4 parameters: (err, req, res, next)
const errorHandler = (err, req, res, next) => {
  console.error('Error:', err);
  
  // Set default status code
  const statusCode = err.statusCode || 500;
  const message = err.message || 'Internal Server Error';
  
  // Send error response
  res.status(statusCode).json({
    success: false,
    error: {
      message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
};

// Async error handler wrapper
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage with async/await
app.get('/async-route', asyncHandler(async (req, res) => {
  const data = await someAsyncOperation();
  if (!data) {
    throw new Error('Data not found');
  }
  res.json(data);
}));

// Register error handler (should be last middleware)
app.use(errorHandler);

Complete Express Application Example:

javascript

// app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');

const app = express();

// Middleware stack
app.use(helmet()); // Security headers
app.use(cors()); // CORS enabled
app.use(morgan('dev')); // HTTP request logging
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies

// Custom middleware
app.use((req, res, next) => {
  req.requestTime = new Date().toISOString();
  next();
});

// Routes
app.get('/', (req, res) => {
  res.json({
    message: 'Welcome to Express API',
    timestamp: req.requestTime,
    endpoints: [
      'GET /api/users',
      'POST /api/users',
      'GET /api/users/:id'
    ]
  });
});

// API routes
app.get('/api/users', (req, res) => {
  // Simulate database query
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ];
  res.json(users);
});

app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  
  if (!name || !email) {
    return res.status(400).json({ error: 'Name and email are required' });
  }
  
  // Simulate creating user
  const newUser = {
    id: Date.now(),
    name,
    email
  };
  
  res.status(201).json({
    message: 'User created',
    user: newUser
  });
});

// 404 handler
app.use((req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  const status = err.status || 500;
  const message = err.message || 'Internal Server Error';
  
  res.status(status).json({
    error: {
      message,
      status
    }
  });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

module.exports = app; // For testing

Testing the Express Server:

bash

# Start server
npm run dev

# Test endpoints
curl http://localhost:3000/
curl http://localhost:3000/api/users
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"john@example.com"}'

# Test error
curl http://localhost:3000/nonexistent

Express.js simplifies Node.js web development by providing a clean, middleware-based architecture that makes it easy to handle routes, requests, and responses while maintaining flexibility and performance.

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?