Back to catalog

expressjs-development

Comprehensive Express.js development skill covering routing, middleware, request/response handling, error handling, and building production-ready REST APIs

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: expressjs-development
description: Comprehensive Express.js development skill covering routing, middleware, request/response handling, error handling, and building production-ready REST APIs
category: backend
tags: [expressjs, nodejs, rest-api, middleware, routing, backend, web-server]
version: 1.0.0
context7_library: /expressjs/express
context7_trust_score: 9
---

# Express.js Development Skill

This skill provides comprehensive guidance for building production-ready web applications and REST APIs using Express.js, covering routing, middleware, request/response handling, error handling, authentication, validation, and deployment best practices.

## When to Use This Skill

Use this skill when:
- Building RESTful APIs for web and mobile applications
- Creating backend services and microservices
- Developing web servers with server-side rendering
- Implementing API gateways and proxy servers
- Building real-time applications with WebSocket support
- Creating middleware-based request processing pipelines
- Developing authentication and authorization systems
- Implementing file upload and download services
- Building webhook handlers and integrations
- Creating serverless functions with Express

## Core Concepts

### Application Setup

Express applications are built by creating an instance of Express and configuring middleware and routes.

**Basic Express Application:**
```javascript
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 port ${PORT}`);
});
```

**Application with Configuration:**
```javascript
const express = require('express');
const app = express();

// App settings
app.set('port', process.env.PORT || 3000);
app.set('env', process.env.NODE_ENV || 'development');
app.set('trust proxy', 1); // Trust first proxy

// View engine setup (optional)
app.set('view engine', 'ejs');
app.set('views', './views');

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

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

module.exports = app;
```

### Routing

Routing refers to how an application's endpoints (URIs) respond to client requests.

**Basic Routes:**
```javascript
const express = require('express');
const app = express();

// HTTP Methods
app.get('/users', (req, res) => {
  res.json({ message: 'Get all users' });
});

app.post('/users', (req, res) => {
  res.json({ message: 'Create user' });
});

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

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

// Multiple methods on same route
app.route('/users/:id')
  .get((req, res) => res.json({ message: 'Get user' }))
  .put((req, res) => res.json({ message: 'Update user' }))
  .delete((req, res) => res.json({ message: 'Delete user' }));
```

**Route Parameters:**
```javascript
// Single parameter
app.get('/users/:userId', (req, res) => {
  const { userId } = req.params;
  res.json({ userId });
});

// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  res.json({ userId, postId });
});

// Optional parameters with regex
app.get('/users/:userId/posts/:postId?', (req, res) => {
  // postId is optional
  res.json(req.params);
});

// Parameter validation
app.param('userId', (req, res, next, id) => {
  // Validate or transform parameter
  if (!id.match(/^\d+$/)) {
    return res.status(400).json({ error: 'Invalid user ID' });
  }
  req.userId = parseInt(id);
  next();
});
```

**Query Strings:**
```javascript
// GET /search?q=express&limit=10&page=2
app.get('/search', (req, res) => {
  const { q, limit = 20, page = 1 } = req.query;
  res.json({
    query: q,
    limit: parseInt(limit),
    page: parseInt(page)
  });
});
```

**Router Modules:**
```javascript
// routes/users.js
const express = require('express');
const router = express.Router();

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

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

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

module.exports = router;

// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);
```

### Middleware

Middleware functions have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle.

**Application-Level Middleware:**
```javascript
// Executed for every request
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

// Executed for specific path
app.use('/api', (req, res, next) => {
  req.startTime = Date.now();
  next();
});

// Multiple middleware functions
app.use(
  express.json(),
  express.urlencoded({ extended: true }),
  cookieParser()
);
```

**Router-Level Middleware:**
```javascript
const router = express.Router();

// Middleware for all routes in this router
router.use((req, res, next) => {
  console.log('Router middleware');
  next();
});

// Middleware for specific route
router.get('/users',
  authMiddleware,
  validationMiddleware,
  (req, res) => {
    res.json({ users: [] });
  }
);
```

**Built-in Middleware:**
```javascript
// 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'));
app.use('/uploads', express.static('uploads'));
```

**Third-Party Middleware:**
```javascript
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');

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

// CORS
app.use(cors({
  origin: 'https://example.com',
  credentials: true
}));

// Logging
app.use(morgan('combined'));

// Compression
app.use(compression());
```

**Custom Middleware:**
```javascript
// Request logging middleware
function requestLogger(req, res, next) {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
  });

  next();
}

// Authentication middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

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

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// Request validation middleware
function validateUser(req, res, next) {
  const { email, password } = req.body;

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

  if (!email.includes('@')) {
    return res.status(400).json({ error: 'Invalid email' });
  }

  next();
}

app.use(requestLogger);
app.post('/login', validateUser, loginHandler);
app.get('/protected', requireAuth, protectedHandler);
```

### Request Object

The request object represents the HTTP request and has properties for query strings, parameters, body, headers, etc.

**Request Properties:**
```javascript
app.post('/api/users/:id', (req, res) => {
  // Route parameters
  const { id } = req.params;

  // Query string
  const { sort, filter } = req.query;

  // Request body
  const { name, email } = req.body;

  // Headers
  const userAgent = req.get('User-Agent');
  const contentType = req.get('Content-Type');

  // Request info
  const method = req.method;
  const path = req.path;
  const url = req.url;
  const baseUrl = req.baseUrl;
  const protocol = req.protocol;
  const hostname = req.hostname;
  const ip = req.ip;

  // Cookies (requires cookie-parser)
  const { sessionId } = req.cookies;

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

**Request Methods:**
```javascript
app.post('/upload', (req, res) => {
  // Check content type
  if (req.is('application/json')) {
    // Handle JSON
  }

  // Check accept header
  if (req.accepts('json')) {
    res.json({ data: 'json response' });
  } else if (req.accepts('html')) {
    res.send('<html>html response</html>');
  }

  // Get header value
  const auth = req.get('Authorization');

  // Get range header
  const range = req.range(1000);
});
```

### Response Object

The response object represents the HTTP response that an Express app sends when it gets an HTTP request.

**Sending Responses:**
```javascript
app.get('/api/data', (req, res) => {
  // Send JSON
  res.json({ message: 'Success', data: [] });

  // Send string
  res.send('Hello World');

  // Send status
  res.sendStatus(200); // Equivalent to res.status(200).send('OK')

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

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

  // Render view
  res.render('index', { title: 'Home' });

  // Redirect
  res.redirect('/login');
  res.redirect(301, 'https://example.com');

  // End response
  res.end();
});
```

**Setting Status and Headers:**
```javascript
app.get('/api/resource', (req, res) => {
  // Set status code
  res.status(201).json({ created: true });

  // Set headers
  res.set('Content-Type', 'application/json');
  res.set({
    'X-API-Version': '1.0',
    'X-Rate-Limit': '100'
  });

  // Set cookie
  res.cookie('name', 'value', {
    maxAge: 900000,
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
  });

  // Clear cookie
  res.clearCookie('name');

  res.json({ success: true });
});
```

**Response Formats:**
```javascript
app.get('/api/users/:id', (req, res) => {
  const user = { id: 1, name: 'John' };

  res.format({
    'text/plain': () => {
      res.send(`${user.name}`);
    },
    'text/html': () => {
      res.send(`<p>${user.name}</p>`);
    },
    'application/json': () => {
      res.json(user);
    },
    default: () => {
      res.status(406).send('Not Acceptable');
    }
  });
});
```

### Error Handling

Error-handling middleware functions have four arguments: (err, req, res, next).

**Error-Handling Middleware:**
```javascript
// 404 handler
app.use((req, res, next) => {
  res.status(404).json({ error: 'Not found' });
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);

  res.status(err.status || 500).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});
```

**Async Error Handling:**
```javascript
// Async wrapper utility
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Using async wrapper
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);

  if (!user) {
    const error = new Error('User not found');
    error.status = 404;
    throw error;
  }

  res.json(user);
}));

// Custom error classes
class AppError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

class NotFoundError extends AppError {
  constructor(message = 'Resource not found') {
    super(message, 404);
  }
}

class ValidationError extends AppError {
  constructor(message = 'Validation failed') {
    super(message, 400);
  }
}
```

## API Reference

### Express Application Methods

**app.use([path], middleware)**
- Mounts middleware at the specified path
- If path is not specified, middleware is executed for every request

**app.METHOD(path, [middleware...], handler)**
- Routes HTTP requests (GET, POST, PUT, DELETE, etc.)
- Multiple middleware functions can be specified

**app.route(path)**
- Returns an instance of a single route for chaining HTTP verbs

**app.listen(port, [hostname], [backlog], [callback])**
- Binds and listens for connections on the specified host and port

**app.param(name, callback)**
- Adds callback triggers to route parameters

**app.set(name, value)**
- Assigns setting name to value

**app.get(name)**
- Returns the value of setting name

### Router Methods

**router.use([path], middleware)**
- Mounts middleware for the router

**router.METHOD(path, [middleware...], handler)**
- Routes HTTP requests within the router

**router.route(path)**
- Returns a route instance for chaining

**router.param(name, callback)**
- Adds parameter callbacks

### Request Properties

- **req.body**: Contains parsed request body (requires body-parser)
- **req.params**: Route parameters
- **req.query**: Parsed query string
- **req.headers**: Request headers
- **req.cookies**: Cookies (requires cookie-parser)
- **req.method**: HTTP method
- **req.path**: Request path
- **req.url**: Full URL
- **req.ip**: Remote IP address
- **req.protocol**: Request protocol (http or https)

### Request Methods

- **req.get(header)**: Returns header value
- **req.is(type)**: Checks if content type matches
- **req.accepts(types)**: Checks if types are acceptable
- **req.range(size)**: Parses range header

### Response Methods

- **res.json(obj)**: Sends JSON response
- **res.send(body)**: Sends response
- **res.status(code)**: Sets status code
- **res.sendStatus(code)**: Sets status and sends status message
- **res.set(field, value)**: Sets response header
- **res.cookie(name, value, options)**: Sets cookie
- **res.clearCookie(name)**: Clears cookie
- **res.redirect([status], path)**: Redirects to path
- **res.render(view, locals)**: Renders view template
- **res.sendFile(path)**: Sends file
- **res.download(path, filename)**: Downloads file

## Workflow Patterns

### REST API Design

**Complete REST API Example:**
```javascript
const express = require('express');
const router = express.Router();

// GET /api/users - List all users
router.get('/', asyncHandler(async (req, res) => {
  const { page = 1, limit = 10, sort = 'createdAt' } = req.query;

  const users = await User.find()
    .sort(sort)
    .limit(parseInt(limit))
    .skip((parseInt(page) - 1) * parseInt(limit))
    .select('-password');

  const total = await User.countDocuments();

  res.json({
    data: users,
    pagination: {
      page: parseInt(page),
      limit: parseInt(limit),
      total,
      pages: Math.ceil(total / limit)
    }
  });
}));

// GET /api/users/:id - Get single user
router.get('/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id).select('-password');

  if (!user) {
    throw new NotFoundError('User not found');
  }

  res.json({ data: user });
}));

// POST /api/users - Create user
router.post('/',
  validateUser,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already exists');
    }

    const user = await User.create({ email, password, name });

    res.status(201).json({
      data: user.toJSON(),
      message: 'User created successfully'
    });
  })
);

// PUT /api/users/:id - Update user
router.put('/:id',
  requireAuth,
  validateUserUpdate,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    ).select('-password');

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({
      data: user,
      message: 'User updated successfully'
    });
  })
);

// DELETE /api/users/:id - Delete user
router.delete('/:id',
  requireAuth,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndDelete(req.params.id);

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({ message: 'User deleted successfully' });
  })
);

module.exports = router;
```

### Authentication

**JWT Authentication:**
```javascript
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

// Register
router.post('/register',
  validateRegistration,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    // Check if user exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already registered');
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create user
    const user = await User.create({
      email,
      password: hashedPassword,
      name
    });

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.status(201).json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Login
router.post('/login',
  validateLogin,
  asyncHandler(async (req, res) => {
    const { email, password } = req.body;

    // Find user
    const user = await User.findOne({ email });
    if (!user) {
      throw new ValidationError('Invalid credentials');
    }

    // Verify password
    const isValid = await bcrypt.compare(password, user.password);
    if (!isValid) {
      throw new ValidationError('Invalid credentials');
    }

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Refresh token
router.post('/refresh',
  asyncHandler(async (req, res) => {
    const { refreshToken } = req.body;

    if (!refreshToken) {
      throw new ValidationError('Refresh token required');
    }

    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);

    const token = jwt.sign(
      { userId: decoded.userId, email: decoded.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({ data: { token } });
  })
);

// Auth middleware
function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new AuthenticationError('No token provided');
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    throw new AuthenticationError('Invalid token');
  }
}

// Role-based authorization
function requireRole(...roles) {
  return async (req, res, next) => {
    const user = await User.findById(req.user.userId);

    if (!user || !roles.includes(user.role)) {
      throw new ForbiddenError('Insufficient permissions');
    }

    next();
  };
}
```

### Validation

**Input Validation with express-validator:**
```javascript
const { body, param, query, validationResult } = require('express-validator');

// Validation middleware
const validate = (validations) => {
  return async (req, res, next) => {
    await Promise.all(validations.map(validation => validation.run(req)));

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        error: 'Validation failed',
        details: errors.array()
      });
    }

    next();
  };
};

// User validation rules
const userValidationRules = {
  create: validate([
    body('email')
      .isEmail()
      .normalizeEmail()
      .withMessage('Invalid email address'),
    body('password')
      .isLength({ min: 8 })
      .withMessage('Password must be at least 8 characters')
      .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
      .withMessage('Password must contain uppercase, lowercase, and number'),
    body('n