Last updated:
0 purchases
lemonade
lemonade #
Simple yet powerful library for data validation:
🔧 Universal: Can be used for both standard data structures and any custom data
🧩 Extensible: Functionality can be easily expanded to any synchronous validation use case
🛡️ Safe: No more runtime bugs because of typos or null values in JSON
✅ More than 80% of tests coverage
✏️ Written in pure dart
🎈 0 dependencies
Main source of inspiration is JSON Schema and ajv library.
What is validation? #
Validation - is a way to ensure that the data being used by the application is correct and can be used safely.
It means that all properties of object are specified and look the way you would expect.
Let's take an example of JSON data validation:
// Correct JSON
{
"name": "John",
"age": 25,
"email": "[email protected]"
}
copied to clipboard
// Incorrect JSON
{
"name": "John",
"age": "25", // String instead of number
"email": "[email protected]"
}
copied to clipboard
If you write a validator for such object, you can 100% sure that data would look like
Usage #
For example, you want to validate GeoJSON point object that looks like this:
{
"type": "Point",
"coordinates": [125.6, 10.1]
}
copied to clipboard
Validator for this schema will look like that:
final pointsValidator = Validator.object(
items: {
// "type": "Point"
'type': Validator.equals('Point'),
// "coordinates": [125.6, 10.1]
'coordinates': Validator.list(
item: Validator.number(),
// Length exactly 2
minItems: 2,
maxItems: 2,
),
},
);
copied to clipboard
Then you can check if any data looks like with very basic method:
final decodedData = jsonDecode('{"type":"Point","coordinates":[125.6,10.1]}');
print(pointsValidator.validate(decodedData)); // true
copied to clipboard
If you want to check, what exactly is wrong, you can use getError method:
final wrongData = jsonDecode('{"type":"Point","coordinates":[125.6,"10.1"]}');
print(pointsValidator.getError(wrongData)); // object.coordinates > list<number>[1] > expected(number).got(10.1)
copied to clipboard
String representation of the errors says that:
In field "coordinates"
At index [1]
Expected any number
Got "10.1"
It's easy to decode and trace an error in your data, isn't it?
Common patterns #
Most of the time you come across some common data types: UUID, IP addresses, hexadecimal strings, etc.
You can easily validate such data using this library!
Here is Validators class for this:
final stringToCheck = 'd87955f3-42a8-43db-aea2-78c0e29e5d23';
Validators.uuid().validate(stringToCheck);
copied to clipboard
It also can be easily used in complex and nested validators:
final structureToCheck = {
'ip': '127.0.0.1',
'mac': '0C:D3:86:34:34:66',
};
final validator = Validator.object(
items: {
'ip': Validators.ipv4(),
'mac': Validators.mac(),
},
);
validator.validate(structureToCheck);
copied to clipboard
Custom validators #
Sometimes you want to validate some custom data structures (your own classes for example).
lemonade gives you two ways of doing so:
Create universal, reusable validator class
Use Validator.customValue factory
Universal validator class #
lemonade exports some validator abstract classes that you can extend in order to implement custom validators:
Validator, ValueValidator, CollectionValidator, CompoundValidator.
Validation work similar way in all of them:
getError have to be overridden,
if it returns null, data is valid,
if it returns ValidationError, data is invalid.
annotation have to be specified, so you stringify validator and watch, what does it validate.
ValidationError has 2 required fields: expected and actual (like in dart tests).
In actual, you should specify value that was checked.
In expected, you should specify the criteria of valid data.
For example, you want to check that number is even.
Firstly you have to check if data is number, then you have to check evenness.
Implementation would look like this:
class EvennessValidator extends Validator {
EvennessValidator() : super(annotation: 'even number');
@override
ValidationError? getError(dynamic data) {
if (data is! int) {
// We expected number, got something else
return ValidationError(expected: 'number', actual: data);
}
if (!data.isEven) {
// We expected even number, got odd
return ValidationError(expected: 'even number', actual: data);
}
// If everything is ok, return null.
return null;
}
}
copied to clipboard
Then you can use it like this:
// Create a new instance of validator
final validator = EvennessValidator();
// Use default methods of it
validator.validate(14);
copied to clipboard
This approach has major pros:
Validators look and work the same as the default ones
Validation errors are more descriptive and customizable
Validators can be easily reused
But also several cons:
More customization and overall code required
It isn't so easy to use for one-time only validators
Validator.customValue #
If you just want to make things done,
you don't need to understand what exactly is invalid
and this validator will be used exactly one time,
than it would be easier to use simple Validator.customValue factory.
For validator that checks if number is even code would look like this:
// Create a new instance of validator
final validator = Validator.customValue((data) {
if (data is! int) return false;
if (!data.isEven) return false;
return true;
});
// Use default methods of it
validator.validate(14);
copied to clipboard
And that's all! So easy!
So this approach has such pros:
Less code to write
No customization, tuning, descriptions
Can use the same callback as for where method in Iterable
But also there is cons:
Can't be easily reused
Results in less consistent code
Features and roadmap #
I started this project in my own urgent need to write huge amount of JSON validators.
Then I tried to make it more universal, so it can be used in other use cases.
✅ Validators for the most basic types
✅ Built-in validators for most common data types
✅ Built-in validators for custom data structures
❌ Full backwards compatibility with JSON Schema
❌ Automatic HTTP response validation on client-side
❌ Automatic HTTP request validation on server-side
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.