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

1

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!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

👋 Kindness is contagious

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

Okay