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!

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay