Skip to main content
โšก Calmops

Express.js: Building Web Applications

Express.js: Building Web Applications

Express.js is a minimal and flexible Node.js web application framework. This article covers Express.js fundamentals.

Introduction

Express.js provides:

  • Routing system
  • Middleware support
  • Request/response handling
  • Template engines
  • Static file serving

Understanding Express.js helps you:

  • Build web applications
  • Create REST APIs
  • Handle HTTP requests
  • Manage middleware
  • Scale applications

Express.js Setup

Installation and Basic Server

// โœ… Good: Install Express
// npm install express

// โœ… Good: Create basic server
const express = require('express');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

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

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

// โœ… Good: Project structure
// my-app/
// โ”œโ”€โ”€ routes/
// โ”‚   โ”œโ”€โ”€ users.js
// โ”‚   โ””โ”€โ”€ posts.js
// โ”œโ”€โ”€ middleware/
// โ”‚   โ”œโ”€โ”€ auth.js
// โ”‚   โ””โ”€โ”€ errorHandler.js
// โ”œโ”€โ”€ controllers/
// โ”‚   โ”œโ”€โ”€ userController.js
// โ”‚   โ””โ”€โ”€ postController.js
// โ”œโ”€โ”€ models/
// โ”‚   โ”œโ”€โ”€ User.js
// โ”‚   โ””โ”€โ”€ Post.js
// โ”œโ”€โ”€ app.js
// โ”œโ”€โ”€ server.js
// โ””โ”€โ”€ package.json

Routing

Basic Routing

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

// โœ… Good: GET request
app.get('/', (req, res) => {
  res.send('Home page');
});

// โœ… Good: POST request
app.post('/users', (req, res) => {
  const user = req.body;
  res.status(201).json({ id: 1, ...user });
});

// โœ… Good: PUT request
app.put('/users/:id', (req, res) => {
  const userId = req.params.id;
  const updatedUser = req.body;
  res.json({ id: userId, ...updatedUser });
});

// โœ… Good: DELETE request
app.delete('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ message: `User ${userId} deleted` });
});

// โœ… Good: Route parameters
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: 'John' });
});

// โœ… Good: Query parameters
app.get('/search', (req, res) => {
  const query = req.query.q;
  const page = req.query.page || 1;
  res.json({ query, page });
});

// โœ… Good: Route groups
const userRoutes = express.Router();

userRoutes.get('/', (req, res) => {
  res.json([{ id: 1, name: 'John' }]);
});

userRoutes.post('/', (req, res) => {
  res.status(201).json(req.body);
});

app.use('/users', userRoutes);

Route Organization

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

router.get('/', (req, res) => {
  res.json([{ id: 1, name: 'John' }]);
});

router.get('/:id', (req, res) => {
  res.json({ id: req.params.id, name: 'John' });
});

router.post('/', (req, res) => {
  res.status(201).json(req.body);
});

router.put('/:id', (req, res) => {
  res.json({ id: req.params.id, ...req.body });
});

router.delete('/:id', (req, res) => {
  res.json({ message: `User ${req.params.id} deleted` });
});

module.exports = router;

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

const app = express();

app.use(express.json());
app.use('/users', userRoutes);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Middleware

Understanding Middleware

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

// โœ… Good: Application-level middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

// โœ… Good: Middleware with conditions
app.use('/api', (req, res, next) => {
  console.log('API request');
  next();
});

// โœ… Good: Error handling middleware
app.use((err, req, res, next) => {
  console.error('Error:', err);
  res.status(500).json({ error: 'Internal server error' });
});

// โœ… Good: Route-specific middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
};

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

// โœ… Good: Multiple middleware
app.get(
  '/admin',
  authenticate,
  (req, res, next) => {
    console.log('Admin route');
    next();
  },
  (req, res) => {
    res.json({ message: 'Admin panel' });
  }
);

Custom Middleware

// middleware/auth.js
function authenticate(req, res, next) {
  const token = req.headers.authorization;

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const decoded = verifyToken(token);
    req.user = decoded;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

module.exports = authenticate;

// middleware/errorHandler.js
function errorHandler(err, req, res, next) {
  console.error('Error:', err);

  const status = err.status || 500;
  const message = err.message || 'Internal server error';

  res.status(status).json({ error: message });
}

module.exports = errorHandler;

// app.js
const express = require('express');
const authenticate = require('./middleware/auth');
const errorHandler = require('./middleware/errorHandler');

const app = express();

app.use(express.json());
app.use(authenticate);

app.get('/protected', (req, res) => {
  res.json({ user: req.user });
});

app.use(errorHandler);

app.listen(3000);

Request and Response Handling

Working with Requests and Responses

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

app.use(express.json());

// โœ… Good: Access request data
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  const userId = req.params.id;
  const query = req.query.filter;
  const token = req.headers.authorization;

  console.log('Body:', req.body);
  console.log('Params:', req.params);
  console.log('Query:', req.query);
  console.log('Headers:', req.headers);

  res.json({ name, email });
});

// โœ… Good: Send different response types
app.get('/json', (req, res) => {
  res.json({ message: 'JSON response' });
});

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

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

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

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

// โœ… Good: Set status codes
app.post('/users', (req, res) => {
  res.status(201).json({ id: 1, ...req.body });
});

app.get('/not-found', (req, res) => {
  res.status(404).json({ error: 'Not found' });
});

app.get('/error', (req, res) => {
  res.status(500).json({ error: 'Server error' });
});

// โœ… Good: Set headers
app.get('/custom-header', (req, res) => {
  res.set('X-Custom-Header', 'value');
  res.json({ message: 'Response with custom header' });
});

Static Files and Views

Serving Static Files

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

// โœ… Good: Serve static files
app.use(express.static('public'));
app.use(express.static(path.join(__dirname, 'public')));

// โœ… Good: Serve static files with prefix
app.use('/static', express.static('public'));

// โœ… Good: Set view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// โœ… Good: Render views
app.get('/', (req, res) => {
  res.render('index', { title: 'Home', user: 'John' });
});

app.listen(3000);

Error Handling

Comprehensive Error Handling

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

// โœ… Good: Try-catch in routes
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await getUser(req.params.id);
    res.json(user);
  } catch (err) {
    next(err);
  }
});

// โœ… Good: Error handling middleware
app.use((err, req, res, next) => {
  console.error('Error:', err);

  if (err.status === 404) {
    return res.status(404).json({ error: 'Not found' });
  }

  if (err.status === 401) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  res.status(500).json({ error: 'Internal server error' });
});

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

app.listen(3000);

Best Practices

  1. Use environment variables:

    // โœ… Good: Environment variables
    const PORT = process.env.PORT || 3000;
    const NODE_ENV = process.env.NODE_ENV || 'development';
    
    // โŒ Bad: Hardcoded values
    const PORT = 3000;
    
  2. Organize routes:

    // โœ… Good: Separate route files
    const userRoutes = require('./routes/users');
    app.use('/users', userRoutes);
    
    // โŒ Bad: All routes in one file
    app.get('/users', ...);
    app.post('/users', ...);
    
  3. Use middleware properly:

    // โœ… Good: Middleware order
    app.use(express.json());
    app.use(authenticate);
    app.use(errorHandler);
    
    // โŒ Bad: Wrong order
    app.use(errorHandler);
    app.use(authenticate);
    

Summary

Express.js is powerful. Key takeaways:

  • Set up Express servers
  • Create routes for different HTTP methods
  • Use middleware for cross-cutting concerns
  • Handle requests and responses
  • Serve static files
  • Implement error handling
  • Organize code properly
  • Build scalable APIs

Next Steps

Comments