DEV Community

Cover image for Mastering Field Validation and User Existence Checks in Node.js: A Developer’s Survival Guide
Yug Jadvani
Yug Jadvani

Posted on

Mastering Field Validation and User Existence Checks in Node.js: A Developer’s Survival Guide

The Night the Database Cried

It was 3 AM when my phone buzzed violently. Our production database had become a graveyard of empty user profiles. A registration endpoint had silently accepted null values for months. "How?" I asked my sleep-deprived self. The answer? We’d forgotten to validate required fields and check user existence properly.

This is why field validation and user existence checks aren’t just checkboxes — they’re your first line of defense against data chaos. Let me show you how to implement these safeguards in Node.js, learned through years of firefighting production issues.


Step 1: Setting the Stage: Project Directory

To get started, here’s our folder structure:

cd src && mkdir utils
cd utils && touch validateRequiredFields.ts checkUserExists.ts
Enter fullscreen mode Exit fullscreen mode

We’ll create two essential modules:

  1. validateRequiredFields.ts for managing fields validation.
  2. checkUserExists.ts for handling user exists validation.

Step 2: Validating Required Fields — Your Data Bouncer

Problem Solved: Prevents API endpoints from processing incomplete/invalid requests that can corrupt your data or crash services.

Implementation:

Inside of validateRequiredFields.ts:

// utils/validateRequiredFields.ts
interface ValidationResult {
  isValid: boolean;
  error?: string;
}

/**
 * Validates required fields in a request
 * @param fields - Object containing field names and their values
 * @returns ValidationResult indicating if all required fields are present
 * @example
 * const validation = validateRequiredFields({ email: 'user@example.com', password: '' });
 * if (!validation.isValid) {
 *   // Handle missing fields
 * }
 */
export const validateRequiredFields = (fields: Record<string, any>): ValidationResult => {
  const missingFields = Object.entries(fields)
    .filter(([_, value]) => !value?.toString().trim())
    .map(([key]) => key);

  if (missingFields.length > 0) {
    return {
      isValid: false,
      error: `Missing required fields: ${missingFields.join(', ')}`,
    };
  }

  return { isValid: true };
};
Enter fullscreen mode Exit fullscreen mode

Pro Tip: Always combine this with schema validation (like Zod or Joi) for complex validation rules. I learned this the hard way when someone submitted a password field containing just spaces!

Usage in Express Route:

// routes/auth.ts
app.post('/register', async (req, res) => {
  const { email, password } = req.body;

  // Validate fields
  const validation = validateRequiredFields({ email, password });
  if (!validation.isValid) {
    return res.status(400).json({ error: validation.error });
  }

  // Continue with registration
});
Enter fullscreen mode Exit fullscreen mode

Step 3: User Existence Checks — The Gatekeeper

Problem Solved: Prevents duplicate accounts and ensures operations only affect existing users.

Implementation:

Inside of checkUserExists.ts:

// utils/checkUserExists.ts
import pool from '../db/db';

interface CheckResult {
  exists: boolean;
  userData?: any;
}

/**
 * Checks if a user exists in the database
 * @param email - User's email address
 * @param expectUserToExist - If true, throws error when user doesn't exist; if false, throws when user does exist
 * @returns Object containing existence status and optional user data
 * @throws Error when user existence doesn't match expectation
 */
export const checkUserExists = async (
  email: string,
  shouldExist: boolean = true
): Promise<CheckResult> => {
  const userExists = await pool.query(
    'SELECT * FROM users WHERE email = $1 LIMIT 1', 
    [email.toLowerCase().trim()]
  );

  const exists = rows.length > 0;

  if (shouldExist && !exists) throw new Error('User not found');
  if (!shouldExist && exists) throw new Error('Email already registered');

  return { exists, userData: exists ? userExists.rows[0] : undefined };
};
Enter fullscreen mode Exit fullscreen mode

Critical Insight: Always normalize emails to lowercase and trim whitespace. I once spent 4 hours debugging a “user doesn’t exist” error that turned out to be “USER@DOMAIN.COM” vs “user@domain.com”.

Usage Flow:

// Registration endpoint
app.post('/register', async (req, res) => {
  try {
    await checkUserExists(email, false); // Expect no existing user

    // Create new user
  } catch (error) {
    return res.status(409).json({ error: error.message });
  }
});

// Login endpoint
app.post('/login', async (req, res) => {
  try {
    const { userData } = await checkUserExists(email, true);
    if (!userData.is_verified) {
      return res.status(403).json({ error: 'Verify your email first' });
    }

    // Continue login
  } catch (error) {
    return res.status(404).json({ error: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode

Why This Matters: The Three-Layer Defense

  1. Client-Side Validation: Basic checks in the UI
  2. Field Validation: Your API’s first reality check
  3. Database Checks: The final authority

Without this trifecta, you’re vulnerable to:

  • Duplicate accounts wasting storage
  • Ghost users cluttering analytics
  • Security holes from unverified operations

The Production-Proven Approach

After implementing these patterns across 12+ services, here’s my battle-tested advice:

  1. Centralize Validation: Create reusable modules like these
  2. Standardize Errors: Use consistent error formats
  3. Log Validation Failures: They’re early warning signs
  4. Test Boundary Cases: Empty strings, nulls, whitespace-only values
// Test case that saved me last week
test('rejects password with only spaces', () => {
  const result = validateRequiredFields({
    password: '    '
  });
  expect(result.isValid).toBe(false);
});
Enter fullscreen mode Exit fullscreen mode

Your Challenge This Week:

Audit one authentication endpoint. How many of these checks are missing? Implement these utilities and watch your error logs shrink like magic.

Remember: In the world of Node.js APIs, good validation isn’t just code — it’s a love letter to your future self.


Before You Go… 🚀

Thanks for sticking around!

Top comments (0)