Skip to content

tobyemmanuel/validlyjs

Repository files navigation

ValidlyJS

ValidlyJS

A high-performance Laravel-inspired validation library for TypeScript/JavaScript

npm version License: MIT TypeScript Build Status

✨ Features

  • πŸš€ Laravel-Style Validation: Define rules using a fluent API or Laravel-style strings
  • πŸ”’ Type-Safe: Built with TypeScript for robust type checking and inference
  • 🌐 Frontend-Focused: Designed for browser environments with support for file validation
  • 🎨 Customizable: Add custom rules, error messages, and localization
  • ⚑ Framework-Agnostic: Works with vanilla JavaScript, React, Vue, Node.js, and more
  • πŸ”„ Async Support: Validate data asynchronously for complex use cases
  • πŸ“± Cross-Platform: Browser, Node.js, and mobile app support
  • 🎯 High Performance: Optimized for speed with minimal bundle size

πŸ“¦ Installation

Install ValidlyJS via npm:

npm install validlyjs

Or via yarn:

yarn add validlyjs

πŸš€ Quick Start

Basic Validation

import { Validator, string, number } from 'validlyjs';

const validator = new Validator({
  name: string().required().min(3),
  age: number().min(18).max(120),
  email: string().email().required()
});

const data = {
  name: "John",
  age: 25,
  email: "john@example.com"
};

const result = await validator.validate(data);

if (!result.isValid) {
  console.log("Validation Errors:", result.errors);
} else {
  console.log("Data is valid!");
}

Laravel-Style String Validation

import { Validator } from 'validlyjs';

const validator = new Validator({
  username: "required|min:3|max:20|alpha",
  email: "required|email",
  password: "required|min:8|confirmed"
});

const result = await validator.validate({
  username: "john_doe",
  email: "john@example.com",
  password: "secretpassword",
  password_confirmation: "secretpassword"
});

Array Format Validation

const validator = new Validator({
  password: ["required", "min:8", "max:20"],
  tags: ["array", "min:1", "max:5"]
});

πŸ“š Validation Types

String Validation

import { string } from 'validlyjs';

const schema = {
  username: string()
    .required()
    .min(3)
    .max(20)
    .alpha()
    .lowercase(),
  
  email: string()
    .required()
    .email()
    .max(255),
    
  url: string()
    .url()
    .nullable(),
    
  phone: string()
    .regex(/^\+?[1-9]\d{1,14}$/)
};

Number Validation

import { number } from 'validlyjs';

const schema = {
  age: number()
    .required()
    .min(18)
    .max(120)
    .integer(),
    
  price: number()
    .required()
    .min(0)
    .decimal(2),
    
  rating: number()
    .between(1, 5)
};

Array & Object Validation

import { array, object, string, number } from 'validlyjs';

const schema = {
  tags: array()
    .required()
    .min(1)
    .max(5)
    .unique(),
    
  user: object()
    .required()
    .shape({
      name: string().required(),
      age: number().min(18),
      preferences: object().shape({
        theme: string().in(['light', 'dark']),
        notifications: boolean()
      })
    })
};

Date Validation

import { date } from 'validlyjs';

const schema = {
  birthDate: date()
    .required()
    .before('today')
    .after('1900-01-01'),
    
  appointmentDate: date()
    .required()
    .after('today')
    .format('YYYY-MM-DD')
};

File Validation

import { file } from 'validlyjs';

const schema = {
  avatar: file()
    .required()
    .maxSize('2MB')
    .mimes(['jpg', 'png', 'gif'])
    .dimensions('min_width=100,min_height=100'),
    
  document: file()
    .mimes(['pdf', 'doc', 'docx'])
    .maxSize('10MB')
};

πŸ”„ Async Validation

const validator = new Validator({
  username: string()
    .required()
    .min(3)
    .async(async (value) => {
      const isAvailable = await checkUsernameAvailability(value);
      return isAvailable ? true : "Username is already taken";
    }),
    
  email: string()
    .required()
    .email()
    .async(async (value) => {
      const exists = await checkEmailExists(value);
      return !exists ? true : "Email is already registered";
    })
});

const result = await validator.validateAsync({
  username: "john_doe",
  email: "john@example.com"
});

🎯 Conditional Validation

const validator = new Validator({
  // Required if another field has a specific value
  phone: string().requiredIf('contact_method', 'phone'),
  
  // Required unless another field has a specific value
  email: string().requiredUnless('contact_method', 'phone'),
  
  // Required with other fields
  city: string().requiredWith(['state', 'country']),
  
  // Prohibited if another field has a specific value
  discount: number().prohibitedIf('user_type', 'premium'),
  
  // Conditional validation with when()
  items: array().required().min(1),
  'items.*': string().when('items', {
    length: { gte: 5 },
    then: string().min(10),
    otherwise: string().min(3)
  })
});

🎨 Framework Integration

React Integration

import { useValidator, string, number } from 'validlyjs/react';

function UserForm() {
  const { validate, errors, isValid } = useValidator({
    name: string().required().min(2),
    email: string().required().email(),
    age: number().required().min(18)
  });

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData);
    
    const result = await validate(data);
    if (result.isValid) {
      // Submit form
      console.log('Form is valid!', data);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" />
      {errors.name && <div className="error">{errors.name[0]}</div>}

      <input name="email" type="email" placeholder="Email" />
      {errors.email && <div className="error">{errors.email[0]}</div>}

      <input name="age" type="number" placeholder="Age" />
      {errors.age && <div className="error">{errors.age[0]}</div>}

      <button type="submit" disabled={!isValid}>Submit</button>
    </form>
  );
}

Vue Integration

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="form.name" placeholder="Name" />
    <span v-if="errors.name" class="error">{{ errors.name[0] }}</span>

    <input v-model="form.email" type="email" placeholder="Email" />
    <span v-if="errors.email" class="error">{{ errors.email[0] }}</span>

    <button type="submit" :disabled="!isValid">Submit</button>
  </form>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useValidator, string } from 'validlyjs/vue';

const form = ref({
  name: '',
  email: '',
});

const { validate, errors, isValid } = useValidator({
  name: string().required().min(2),
  email: string().required().email(),
}, form);

const handleSubmit = async () => {
  const result = await validate(form.value);
  if (result.isValid) {
    console.log('Form is valid!', form.value);
  }
};
</script>

Node.js/Express Integration

import express from 'express';
import { Validator, string, number } from 'validlyjs/node';

const app = express();

app.post('/users', async (req, res) => {
  const validator = new Validator({
    name: string().required().min(2),
    email: string().required().email(),
    age: number().required().min(18)
  });

  const result = await validator.validate(req.body);
  
  if (!result.isValid) {
    return res.status(422).json({
      message: 'Validation failed',
      errors: result.errors
    });
  }

  // Process valid data
  res.json({ message: 'User created successfully' });
});

πŸ› οΈ Customization

Custom Rules

import { Validator, string, extend } from 'validlyjs';

// Define a custom rule
extend(
  "strong_password",
  (value) => {
    return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/.test(value);
  },
  "The :attribute must contain at least one uppercase letter, one lowercase letter, one number, and one special character."
);

// Use the custom rule
const validator = new Validator({
  password: string().required().custom("strong_password")
});

Custom Messages

import { configure } from 'validlyjs';

configure({
  messages: {
    required: 'The :attribute field is required',
    email: 'Please enter a valid email address',
    min: {
      string: 'The :attribute must be at least :min characters',
      numeric: 'The :attribute must be at least :min'
    }
  }
});

Localization

import { configure } from 'validlyjs';

configure({
  locale: 'es',
  messages: {
    required: 'El campo :attribute es obligatorio',
    email: 'El campo :attribute debe ser un email vΓ‘lido',
    min: {
      string: 'El campo :attribute debe tener al menos :min caracteres'
    }
  }
});

πŸ“Š Performance

ValidlyJS is optimized for performance:

  • Tree-shakable: Import only what you need
  • Fast validation: Optimized validation algorithms
  • Memory efficient: Minimal memory footprint
  • Async-friendly: Non-blocking validation

πŸ§ͺ Testing

Run the test suite:

npm test

Run tests with coverage:

npm run test:coverage

Run integration tests:

npm run test:integrations

πŸ“– Documentation

For comprehensive documentation, examples, and API reference, visit:

πŸ“š ValidlyJS Documentation

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/tobyemmanuel/validlyjs.git
  3. Install dependencies: npm install
  4. Create a feature branch: git checkout -b feature/your-feature
  5. Make your changes and add tests
  6. Run tests: npm test
  7. Commit your changes: git commit -m 'Add some feature'
  8. Push to the branch: git push origin feature/your-feature
  9. Open a pull request

πŸ“ Changelog

See CHANGELOG.md for a detailed list of changes.

πŸ“„ License

ValidlyJS is open-source software licensed under the MIT License.

πŸ™ Acknowledgments

πŸ“ž Support

⭐ Star us on GitHub β€’ 🌐 Visit our website

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published