Skip to content

[Feature] Add Input Validation to Contact Creation Endpoint #11

Description

@codersgyan

Problem Statement

The POST /api/contacts endpoint currently accepts any valid JSON payload without proper validation, leading to:

  • Data quality issues - contacts with empty or malformed emails stored in database
  • Inconsistent state - records without any identifying information (no name, no email)
  • Security concerns - potential injection attacks or buffer overflow with unbounded field lengths
  • Poor API experience - clients don't receive clear feedback on invalid input

This creates technical debt and makes the API unreliable for production use.

Current Behavior

The endpoint accepts requests like these without error:

// Empty email - should fail
{
  "email": "",
  "first_name": ""
}

// Invalid email format - should fail
{
  "email": "not-an-email",
  "first_name": "John"
}

// No identifying information - should fail
{
  "phone": "123"
}

// Unbounded field length - should fail
{
  "email": "a@b.c" + ("x" * 1000000),
  "first_name": "John"
}

Currently returns 201 Created for all of these cases.

Proposed Solution

Add server-side validation to the contact creation handler that enforces:

Required Fields

  • Email: Must be provided and match valid email format (RFC 5322 compliant)
  • Name: first_name and last_name must be non-empty

Optional Field Validation (when provided)

  • Email length: Maximum 255 characters
  • Phone number: Valid international format if provided (E.164 or local format)
  • Name fields: Maximum reasonable length (e.g., 100 characters each)

Error Response Format

Return 400 Bad Request with structured error details:

{
  "error": "validation failed",
  "details": [
    "email is required and must be valid",
    "first_name isrequired",
    "last_name is required"
  ]
}

Proposed Implementation

Location

internal/contact/handler.go:19 - There's already a TODO comment marking this spot

Approach

// internal/contact/validator.go (new file)

type ContactValidationError struct {
    Field   string
    Message string
}

func ValidateCreateContactRequest(req *CreateContactRequest) []ContactValidationError {
    var errors []ContactValidationError
    
    // Email validation
    if req.Email == "" {
        errors = append(errors, ContactValidationError{
            Field: "email", 
            Message: "email is required",
        })
    } else if !isValidEmail(req.Email) {
        errors = append(errors, ContactValidationError{
            Field: "email",
            Message: "email must be valid format",
        })
    } else if len(req.Email) > 255 {
        errors = append(errors, ContactValidationError{
            Field: "email",
            Message: "email must not exceed 255 characters",
        })
    }
    
    // Name validation
    if strings.TrimSpace(req.FirstName) == "" && strings.TrimSpace(req.LastName) == "" {
        errors = append(errors, ContactValidationError{
            Field: "name",
            Message: "at least one of first_name or last_name is required",
        })
    }
    
    // Phone validation (optional)
    if req.Phone != "" && !isValidPhone(req.Phone) {
        errors = append(errors, ContactValidationError{
            Field: "phone",
            Message: "phone must be valid format",
        })
    }
    
    return errors
}

Integration in Handler

// internal/contact/handler.go

func (h *Handler) CreateContact(w http.ResponseWriter, r *http.Request) {
    var req CreateContactRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        respondError(w, http.StatusBadRequest, "invalid JSON")
        return
    }
    
    // Add validation here (line 19)
    if errs := ValidateCreateContactRequest(&req); len(errs) > 0 {
        respondValidationError(w, errs)
        return
    }
    
    // Continue with business logic...
}

Implementation Checklist

  • Create internal/contact/validator.go with validation functions
  • Implement email format validation (regex or stdlib)
  • Implement phone number validation (consider library like github.com/nyaruka/phonenumbers)
  • Add field length checks
  • Update handler to call validation before processing
  • Add unit tests for validation logic
  • Add integration tests for endpoint behavior
  • Update API documentation with validation rules

Testing Strategy

Unit Tests (validator_test.go)

func TestValidateCreateContactRequest(t *testing.T) {
    tests := []struct {
        name    string
        req     CreateContactRequest
        wantErr bool
        errMsgs []string
    }{
        {
            name: "valid contact",
            req: CreateContactRequest{
                Email: "test@example.com",
                FirstName: "John",
            },
            wantErr: false,
        },
        {
            name: "empty email",
            req: CreateContactRequest{
                FirstName: "John",
            },
            wantErr: true,
            errMsgs: []string{"email is required"},
        },
        // ... more test cases
    }
}

Integration Tests

  • Test endpoint returns 400 for invalid input
  • Test endpoint returns 201 for valid input
  • Test error message format

Questions / Discussion Points

  1. Email validation strictness: Should we use simple regex or full RFC 5322 compliance?

    • Simple regex is faster but may reject some valid emails
    • RFC 5322 is more accurate but complex
  2. Phone validation: Should we support international formats only, or allow flexible local formats?

    • Suggestion: Accept common formats, don't be too strict initially
  3. Field length limits: Are 255 chars for email and 100 for names reasonable?

    • Based on common database varchar limits
  4. Validation library: Should we use a validation library (e.g., go-playground/validator) or custom code?

    • Custom gives us control but more maintenance
    • Library is battle-tested but adds dependency

Benefits

  • ✅ Better data quality - ensures database contains valid, usable contacts
  • ✅ Clear API contract - clients know exactly what's expected
  • ✅ Easier debugging - validation errors caught early with clear messages
  • ✅ Security - prevents potential exploits via malformed input
  • ✅ Good first issue - isolated scope, touches core concepts (validation, HTTP errors)

Additional Context

Good First Issue: This is an excellent starting point for new contributors because:

  • Clear, well-defined scope
  • Touches important concepts (validation, error handling, testing)
  • Doesn't require deep knowledge of business logic
  • Has visible impact on API quality
  • Low risk of breaking existing functionality

Files to familiarize yourself with:

  • internal/contact/handler.go - Where validation will be added
  • internal/contact/types.go - Request/response structures
  • internal/contact/handler_test.go - Existing test patterns

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions