DEV Community

rabindratamang
rabindratamang

Posted on

Avoiding Pitfalls: Server-Side Validation in Express with OpenAPI & Swagger

Why Server-Side Validation Is Non-Negotiable

While frontend validation improves user experience by catching errors early, it should never be the sole layer of protection. Relying only on frontend validation is a major security flaw because:

  • APIs are meant to be consumed by various clients, including third-party applications.
  • Attackers can bypass the UI and send malicious data directly to the API.
  • Data integrity issues can arise due to incomplete or incorrect input.

Solution: Implement robust server-side validation.


Industry Standards for Server-Side Validation in Express

1. Use a Validation Library

Manually writing validation logic for each request is tedious and error-prone. Instead, use libraries like:

  • Joi (powerful schema-based validation)
  • Express-validator (based on validator.js, integrates well with Express)
  • Yup (often used with TypeScript, works with objects)
Example using Joi
const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().min(3).max(30).required(),
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(18).max(99).required(),
});

const validateUser = (req, res, next) => {
  const { error } = userSchema.validate(req.body);
  if (error) return res.status(400).json({ error: error.details[0].message });
  next();
};

app.post('/users', validateUser, (req, res) => {
  res.json({ message: 'User created successfully!' });
});
Enter fullscreen mode Exit fullscreen mode
Example using express-validator
Example: Validating a User Registration Endpoint
const { body, validationResult } = require('express-validator');

app.post('/api/register', [
  // Validate email
  body('email')
    .isEmail()
    .withMessage('Please provide a valid email address.')
    .normalizeEmail(), // Sanitize: normalize the email format

  // Validate password
  body('password')
    .isLength({ min: 8 })
    .withMessage('Password must be at least 8 characters long.')
    .matches(/[A-Z]/)
    .withMessage('Password must contain at least one uppercase letter.')
    .matches(/[0-9]/)
    .withMessage('Password must contain at least one number.'),

  // Validate username
  body('username')
    .trim() // Sanitize: remove whitespace
    .isLength({ min: 3, max: 20 })
    .withMessage('Username must be between 3 and 20 characters long.')
    .matches(/^[a-zA-Z0-9_]+$/)
    .withMessage('Username can only contain letters, numbers, and underscores.'),
], (req, res) => {
  // Check for validation errors
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  // If validation passes, proceed with business logic
  const { email, password, username } = req.body;
  // Save user to the database, etc.
  res.status(201).json({ message: 'User registered successfully!' });
});
Enter fullscreen mode Exit fullscreen mode

2. Enforce OpenAPI Schema Validation

Since we use OpenAPI (Swagger) for API documentation, we also leverage it for validation. Tools like express-openapi-validator help enforce schema-based validation from our OpenAPI definitions.

Install it:

npm install express-openapi-validator
Enter fullscreen mode Exit fullscreen mode

Use it in Express:

const { OpenApiValidator } = require('express-openapi-validator');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const swaggerDocument = YAML.load('./swagger.yaml');

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

app.use(
  OpenApiValidator.middleware({
    apiSpec: './swagger.yaml',
    validateRequests: true, // Validate request bodies, parameters, and headers
    validateResponses: true, // Validate responses (optional)
  })
);
Enter fullscreen mode Exit fullscreen mode

This ensures that API requests adhere to our OpenAPI specification, reducing inconsistencies and vulnerabilities.

3. Implement Middleware for Reusable Validation

Middleware allows reusing validation logic across multiple routes. For instance, a middleware function can validate user authentication or check request headers before processing data.

Example:

const validateApiKey = (req, res, next) => {
  if (!req.headers['x-api-key']) {
    return res.status(403).json({ error: 'API key is required' });
  }
  next();
};

app.use(validateApiKey);
Enter fullscreen mode Exit fullscreen mode

4. Secure Input Data with Sanitization

Validation should go hand-in-hand with sanitization to prevent SQL injection, XSS, or unwanted input.

Example using express-validator:

const { body } = require('express-validator');

app.post(
  '/comment',
  [
    body('text').trim().escape().notEmpty().withMessage('Comment cannot be empty'),
  ],
  (req, res) => {
    // Process comment
    res.json({ message: 'Comment added' });
  }
);
Enter fullscreen mode Exit fullscreen mode

5. Log Validation Failures for Debugging

When validation fails, logging helps diagnose issues. Use a logging library like winston or pino.

const winston = require('winston');
const logger = winston.createLogger({
  transports: [new winston.transports.Console()],
});

const validateRequest = (schema) => (req, res, next) => {
  const { error } = schema.validate(req.body);
  if (error) {
    logger.error(`Validation Error: ${error.details[0].message}`);
    return res.status(400).json({ error: error.details[0].message });
  }
  next();
};
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Our experience taught us a valuable lesson: server-side validation is not optional. By integrating validation at the API level, we:

  • Prevent malicious input
  • Ensure data integrity
  • Improve API security
  • Maintain consistency across clients
  • Write automated tests to validate your validation logic.

If you're building APIs with Express and OpenAPI, make sure to enforce validation from day one to avoid last-minute surprises.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay