javascript-today

Express Framework Basics

Express is the most popular NodeJS web framework. It simplifies building web servers and APIs by providing a clean, minimal interface on top of the raw http module.

Why Express?

Express makes web development easier:

Task Raw HTTP Express
Routing Manual if/else app.get('/path', ...)
JSON parsing Manual chunks Built-in
Static files Manual fs.readFile express.static()
Middleware Manual Built-in system
Error handling Manual try/catch Centralized

Installing Express

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

# Install Express
npm install express

Your First Express Server

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

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

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

Run it:

node server.js

That’s it! Much simpler than raw HTTP.

Routes

Define routes for different HTTP methods:

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

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

app.get('/about', (req, res) => {
  res.send('<h1>About Page</h1>');
});

// POST request
app.post('/api/users', (req, res) => {
  res.send('User created');
});

// PUT request
app.put('/api/users/:id', (req, res) => {
  res.send(`Updated user ${req.params.id}`);
});

// DELETE request
app.delete('/api/users/:id', (req, res) => {
  res.send(`Deleted user ${req.params.id}`);
});

app.listen(3000);

Sending Responses

Express provides multiple ways to send responses:

app.get('/text', (req, res) => {
  res.send('Plain text response');
});

app.get('/html', (req, res) => {
  res.send('<h1>HTML response</h1>');
});

app.get('/json', (req, res) => {
  res.json({ message: 'JSON response', status: 'ok' });
});

app.get('/status', (req, res) => {
  res.status(201).json({ created: true });
});

app.get('/redirect', (req, res) => {
  res.redirect('/about');
});

app.get('/download', (req, res) => {
  res.download('./files/report.pdf');
});

app.get('/file', (req, res) => {
  res.sendFile('/absolute/path/to/file.html');
});

Route Parameters

Capture dynamic values from URLs:

// URL parameter
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ userId });
});
// GET /users/123 → { userId: '123' }

// Multiple parameters
app.get('/posts/:year/:month/:slug', (req, res) => {
  const { year, month, slug } = req.params;
  res.json({ year, month, slug });
});
// GET /posts/2024/01/hello-world

// Optional parameter
app.get('/products/:category/:subcategory?', (req, res) => {
  res.json(req.params);
});
// GET /products/electronics → { category: 'electronics' }
// GET /products/electronics/phones → { category: 'electronics', subcategory: 'phones' }

Query Parameters

Access query strings from URLs:

app.get('/search', (req, res) => {
  const { q, page, limit } = req.query;
  
  res.json({
    searchTerm: q,
    page: page || 1,
    limit: limit || 10
  });
});
// GET /search?q=nodejs&page=2&limit=20

Middleware

Middleware functions execute between receiving a request and sending a response:

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

// Built-in middleware - parse JSON
app.use(express.json());

// Built-in middleware - parse URL-encoded data
app.use(express.urlencoded({ extended: true }));

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

// Custom middleware - add timestamp
app.use((req, res, next) => {
  req.timestamp = new Date();
  next();
});

// Route using middleware data
app.get('/', (req, res) => {
  res.json({ 
    message: 'Hello',
    timestamp: req.timestamp 
  });
});

app.listen(3000);

Middleware flow:

Request → Middleware 1 → Middleware 2 → Route Handler → Response

Static Files

Serve CSS, images, JavaScript files:

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

// Serve files from 'public' directory
app.use(express.static('public'));

app.listen(3000);

Project structure:

project/
├── server.js
└── public/
    ├── index.html
    ├── style.css
    ├── app.js
    └── images/
        └── logo.png

Access files:

  • http://localhost:3000/index.html
  • http://localhost:3000/style.css
  • http://localhost:3000/images/logo.png

Custom path:

// Serve files from 'public' at '/static' route
app.use('/static', express.static('public'));
// Now: http://localhost:3000/static/style.css

Complete API Example

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

// Middleware
app.use(express.json());

// In-memory data store
let users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' }
];
let nextId = 3;

// GET /api/users - List all users
app.get('/api/users', (req, res) => {
  res.json(users);
});

// GET /api/users/:id - Get single user
app.get('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const user = users.find(u => u.id === id);
  
  if (user) {
    res.json(user);
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

// POST /api/users - Create user
app.post('/api/users', (req, res) => {
  const newUser = {
    id: nextId++,
    name: req.body.name,
    email: req.body.email
  };
  
  users.push(newUser);
  res.status(201).json(newUser);
});

// PUT /api/users/:id - Update user
app.put('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const index = users.findIndex(u => u.id === id);
  
  if (index !== -1) {
    users[index] = { ...users[index], ...req.body };
    res.json(users[index]);
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

// DELETE /api/users/:id - Delete user
app.delete('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const index = users.findIndex(u => u.id === id);
  
  if (index !== -1) {
    const deleted = users.splice(index, 1);
    res.json({ message: 'User deleted', user: deleted[0] });
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

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

// Error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

app.listen(3000, () => {
  console.log('API server running on http://localhost:3000');
});

Test with curl:

# Get all users
curl http://localhost:3000/api/users

# Create user
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","email":"charlie@example.com"}'

# Update user
curl -X PUT http://localhost:3000/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated"}'

# Delete user
curl -X DELETE http://localhost:3000/api/users/2

Request Object (req)

Useful properties and methods:

app.get('/info', (req, res) => {
  console.log('URL:', req.url);              // '/info?name=John'
  console.log('Path:', req.path);            // '/info'
  console.log('Method:', req.method);        // 'GET'
  console.log('Headers:', req.headers);      // { ... }
  console.log('Params:', req.params);        // URL parameters
  console.log('Query:', req.query);          // Query string
  console.log('Body:', req.body);            // POST data (needs middleware)
  console.log('IP:', req.ip);                // Client IP
  console.log('Protocol:', req.protocol);    // 'http' or 'https'
  console.log('Hostname:', req.hostname);    // 'localhost'
  
  res.send('Check console for request details');
});

Response Object (res)

Common methods:

// Send different types
res.send('text or HTML');
res.json({ key: 'value' });
res.sendFile('/path/to/file');
res.download('/path/to/file');
res.redirect('/other-page');

// Set status code
res.status(404).send('Not found');
res.status(201).json({ created: true });

// Set headers
res.set('Content-Type', 'text/html');
res.set({
  'Content-Type': 'application/json',
  'X-Custom-Header': 'value'
});

// Cookies
res.cookie('name', 'value', { maxAge: 900000 });
res.clearCookie('name');

// End response without data
res.end();

Development Tools

Auto-restart with Nodemon

# Install nodemon
npm install -D nodemon

# Add to package.json scripts
{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  }
}

# Run in dev mode
npm run dev

Now the server restarts automatically when you save files!

Environment Variables

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

const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    environment: process.env.NODE_ENV || 'development'
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Run with environment variable:

PORT=8080 node server.js
NODE_ENV=production node server.js

Best Practices

DO:

  • Use middleware for common tasks
  • Handle errors properly
  • Use environment variables for config
  • Use nodemon in development
  • Return appropriate status codes
  • Validate input data

DON’T:

  • Forget error handlers
  • Store sensitive data in code
  • Use synchronous operations
  • Expose internal errors to users
  • Forget to parse request body

Summary

Feature Example
Create app const app = express()
GET route app.get('/path', (req, res) => {})
POST route app.post('/path', (req, res) => {})
Send JSON res.json({ data })
Send status res.status(404).send('Not found')
URL params req.params.id
Query params req.query.search
Request body req.body (needs middleware)
Middleware app.use(express.json())
Static files app.use(express.static('public'))
Start server app.listen(3000)