Node.js Express - Complete Beginner's Guide

Express is the most popular Node.js web framework. It provides a robust set of features for building web applications and APIs quickly and easily.

What is Express? #

Express is a minimal and flexible Node.js web application framework that provides features for web and mobile applications. It’s the de facto standard server framework for Node.js.

Why Use Express? #

  • Minimal and flexible - Unopinionated framework
  • Robust routing - Powerful routing system
  • Middleware support - Extensive middleware ecosystem
  • Fast development - Simple API for rapid prototyping
  • Large community - Thousands of packages and plugins

Installing Express #

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

# Install Express
npm install express

Hello World Server #

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

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

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

Run the server:

node app.js

Basic Routing #

HTTP Methods #

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

// GET request
app.get('/users', (req, res) => {
  res.send('Get all users');
});

// POST request
app.post('/users', (req, res) => {
  res.send('Create a user');
});

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

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

// Handle all methods
app.all('/secret', (req, res) => {
  res.send('Secret area');
});

app.listen(3000);

Route Parameters #

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

// Multiple parameters
app.get('/posts/:postId/comments/:commentId', (req, res) => {
  const { postId, commentId } = req.params;
  res.send(`Post: ${postId}, Comment: ${commentId}`);
});

// Optional parameters with regex
app.get('/users/:id(\\d+)', (req, res) => {
  res.send(`User ID (numbers only): ${req.params.id}`);
});

Query Parameters #

app.get('/search', (req, res) => {
  const { q, page, limit } = req.query;
  res.json({
    query: q,
    page: page || 1,
    limit: limit || 10
  });
});

// GET /search?q=express&page=2&limit=20

Middleware #

Middleware functions have access to request, response, and next function.

Application-Level Middleware #

// Logger middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
});

// Authentication middleware
app.use((req, res, next) => {
  const token = req.headers['authorization'];
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  next();
});

Route-Specific Middleware #

const authenticate = (req, res, next) => {
  const token = req.headers['authorization'];
  if (token === 'valid-token') {
    next();
  } else {
    res.status(403).json({ error: 'Invalid token' });
  }
};

app.get('/protected', authenticate, (req, res) => {
  res.json({ message: 'Protected data' });
});

Built-in Middleware #

// Parse JSON bodies
app.use(express.json());

// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

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

Third-Party Middleware #

const cors = require('cors');
const morgan = require('morgan');
const helmet = require('helmet');

// Enable CORS
app.use(cors());

// HTTP request logger
app.use(morgan('dev'));

// Security headers
app.use(helmet());

Request Object #

app.post('/example', (req, res) => {
  // Request properties
  console.log(req.body);       // Request body (needs express.json())
  console.log(req.params);     // Route parameters
  console.log(req.query);      // Query string parameters
  console.log(req.headers);    // HTTP headers
  console.log(req.method);     // HTTP method
  console.log(req.url);        // Request URL
  console.log(req.path);       // Path part of URL
  console.log(req.ip);         // Client IP address
  console.log(req.cookies);    // Cookies (needs cookie-parser)

  res.send('OK');
});

Response Object #

app.get('/response-examples', (req, res) => {
  // Send text
  res.send('Hello World');

  // Send JSON
  res.json({ message: 'Success', data: [] });

  // Set status code
  res.status(404).send('Not Found');

  // Send file
  res.sendFile('/path/to/file.pdf');

  // Download file
  res.download('/path/to/file.pdf');

  // Redirect
  res.redirect('/new-location');

  // Set headers
  res.set('Content-Type', 'application/json');
  res.send('{"message": "Hello"}');

  // Chain methods
  res.status(200).json({ success: true });
});

Building a REST API #

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

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' }
];

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

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

// CREATE user
app.post('/api/users', (req, res) => {
  const { name, email } = req.body;

  if (!name || !email) {
    return res.status(400).json({ error: 'Name and email required' });
  }

  const newUser = {
    id: users.length + 1,
    name,
    email
  };

  users.push(newUser);
  res.status(201).json(newUser);
});

// UPDATE user
app.put('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));

  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  const { name, email } = req.body;
  if (name) user.name = name;
  if (email) user.email = email;

  res.json(user);
});

// DELETE user
app.delete('/api/users/:id', (req, res) => {
  const index = users.findIndex(u => u.id === parseInt(req.params.id));

  if (index === -1) {
    return res.status(404).json({ error: 'User not found' });
  }

  users.splice(index, 1);
  res.status(204).send();
});

app.listen(3000);

Router #

Organize routes using Express Router:

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

router.get('/', (req, res) => {
  res.send('Get all users');
});

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

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

module.exports = router;

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

app.use('/api/users', usersRouter);

app.listen(3000);

Error Handling #

// Custom error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: {
      message: err.message,
      status: err.status || 500
    }
  });
});

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

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

app.get('/async-route', asyncHandler(async (req, res) => {
  const data = await someAsyncOperation();
  res.json(data);
}));

Template Engines #

Using EJS #

npm install ejs
const express = require('express');
const app = express();

// Set view engine
app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Home Page',
    users: ['Alice', 'Bob', 'Charlie']
  });
});

app.listen(3000);
<!-- views/index.ejs -->
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
  <h1><%= title %></h1>
  <ul>
    <% users.forEach(user => { %>
      <li><%= user %></li>
    <% }); %>
  </ul>
</body>
</html>

Environment Variables #

npm install dotenv
// .env file
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=mysecretkey

// app.js
require('dotenv').config();

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

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

File Uploads #

npm install multer
const express = require('express');
const multer = require('multer');
const app = express();

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const upload = multer({ storage });

app.post('/upload', upload.single('file'), (req, res) => {
  res.json({
    message: 'File uploaded successfully',
    filename: req.file.filename
  });
});

// Multiple files
app.post('/upload-multiple', upload.array('files', 5), (req, res) => {
  res.json({
    message: 'Files uploaded',
    files: req.files.map(f => f.filename)
  });
});

app.listen(3000);

Database Integration #

MongoDB with Mongoose #

npm install mongoose
const express = require('express');
const mongoose = require('mongoose');
const app = express();

app.use(express.json());

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/myapp', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// Define schema
const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model('User', userSchema);

// Routes
app.get('/users', async (req, res) => {
  const users = await User.find();
  res.json(users);
});

app.post('/users', async (req, res) => {
  const user = new User(req.body);
  await user.save();
  res.status(201).json(user);
});

app.listen(3000);

CORS Configuration #

npm install cors
const express = require('express');
const cors = require('cors');
const app = express();

// Enable all CORS requests
app.use(cors());

// Configure CORS
app.use(cors({
  origin: 'http://example.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

// Enable CORS for specific route
app.get('/api/data', cors(), (req, res) => {
  res.json({ data: 'This route has CORS enabled' });
});

app.listen(3000);

Best Practices #

  1. Use environment variables for configuration
  2. Organize routes with Express Router
  3. Use middleware for common functionality
  4. Handle errors properly with error middleware
  5. Validate input before processing
  6. Use async/await for cleaner async code
  7. Log requests for debugging
  8. Secure your app with helmet
  9. Use compression for responses
  10. Set appropriate status codes

Common Middleware Packages #

npm install helmet cors morgan compression cookie-parser express-validator
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const compression = require('compression');
const cookieParser = require('cookie-parser');

const app = express();

app.use(helmet());           // Security headers
app.use(cors());             // Enable CORS
app.use(morgan('dev'));      // Request logging
app.use(compression());      // Compress responses
app.use(express.json());     // Parse JSON
app.use(cookieParser());     // Parse cookies

app.listen(3000);

Express makes building web applications and APIs straightforward. Master these fundamentals and you can build production-ready Node.js applications.