
Eskema
Eskema is a small, composable runtime validation library for Dart. It helps you validate dynamic values (JSON, Maps, Lists, primitives) with readable validators and clear error messages.
Use cases
Here are some common usecases for Eskema:
- Validate untyped API JSON before mapping to models (catch missing/invalid fields early).
- Guard inbound request payloads (HTTP handlers, jobs) with clear, fail-fast errors.
- Validate runtime config and feature flags from files or remote sources.
Install
dart pub add eskema
Quick start
Validate a map using a schema-like validator and read a detailed result or a boolean.
1. Create a simple eskema
import 'package:eskema/eskema.dart';
final userValidator = eskema({
'username': isString(),
// Combine validators using `all` and `any`
'age': all([isInt(), isGte(0)]),
// or use operators for simplicity:
'theme': (isString() & (isEq('light') | isEq('dark'))).nullable(),
// Some zero-arg validators also have canonical aliases: e.g. `$isBool`, `$isString`
'premium': nullable($isBool),
// Make a validator nullable
'email': stringMatchesPattern(
RegExp(r"^[^@\s]+@[^@\s]+\.[^@\s]+$"),
error: 'a valid email address',
).nullable(),
});
2. Validate the eskema
The simplest way to check if a validator is valid, is by using the isValid method:
final ok = userValidator.isValid({ 'username': 'bob', 'age': 42 });
print("User is valid: $ok"); // true
You can use the .validate method to get a descriptive error message:
final res = userValidator.validate({
'username': 'alice',
'age': -1,
});
print(res); // false - "Expected age -> greater than or equal to 0, got -1"
You can also make the validation throw
try {
userValidator.validateOrThrow({'username': 'bob'});
} catch (e) {
print(e); // ValidatorFailedException with a helpful message
}
Table of contents
API overview
Check the docs for the technical documentation.
-
Core
IEskValidator/EskValidator— object-based validators that returnEskResultEskResult—.isValid,.isNotValid,.expected,.value, nicetoString()
-
Common validators (examples)
- Types:
isType<T>()e.g.isType<String>(); shorthands:isString(),isInt(),isDouble(),isBool()- Nullability:
isNull(); make any validator nullable withnullable(v)orv.nullable() - Numbers:
isGt(n),isGte(n),isLt(n),isLte(n) - Equality:
isEq(value), deep equalityisDeepEq(value) - Strings:
stringIsOfLength(n),stringContains(sub),stringNotContains(sub),stringMatchesPattern(pattern) - Lists:
listIsOfLength(n),listEach(itemValidator),eskemaList([...]) - Maps:
eskema({ 'key': validator, ... })
- Nullability:
- Types:
-
Composition
all([...])— AND composition (stops on first failure)any([...])— OR composition (passes if any succeed)not(v)— invert a validatornullable(v)orv.nullable()— allownull
-
Results & helpers
.validate(value)→EskResult.isValid(value)/.isNotValid(value)→ bool.validateOrThrow(value)throws on invalid input
Tip: Some zero-arg validators also have canonical aliases (e.g. $isString, $isBool) for concise usage.
Examples
Custom validators
Zero-arg validator
final isHelloWorld = all([
$isString,
EskValidator((value) => EskResult(
isValid: value == 'Hello world',
expected: 'Hello world',
value: value,
)),
]);
print(isHelloWorld.isValid('Hello world')); // true
print(isHelloWorld.validate('hey')); // false - 'Expected Hello world, got "hey"'
Validator with args
IEskValidator isInRange(num min, num max) {
return all([
isType<num>(),
EskValidator((value) => EskResult(
isValid: value >= min && value <= max,
expected: 'number to be between $min and $max',
value: value,
)),
]);
}
print(isInRange(0, 5).isValid(2)); // true
print(isInRange(0, 5).validate(6)); // false - "Expected number to be between 0 and 5, got 6"
Class-based validators
Prefer a class for complex/structured validation? Use EskMap with EskField.
import 'package:eskema/eskema.dart';
enum Theme { light, dark }
class SettingsValidator extends EskMap {
final theme = EskField(
id: 'theme',
validators: [isOneOf(Theme.values)],
);
final notificationsEnabled = EskField(
id: 'notificationsEnabled',
nullable: true,
validators: [isBool()],
);
SettingsValidator({required super.id, super.nullable});
@override
get fields => [theme, notificationsEnabled];
}
class UserValidator extends EskMap {
final name = EskField(id: 'name', validators: [isString()]);
final settings = SettingsValidator(id: 'settings', nullable: true);
@override
get fields => [name, settings];
}
final v = UserValidator();
final result = v.validate({ 'name': 'Test', 'settings': { 'theme': Theme.dark } });
print(result.isValid); // true
Validate untyped API JSON
import 'package:eskema/eskema.dart';
final apiUser = eskema({
'id': isInt(),
'email': stringMatchesPattern(
RegExp(r'^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$'),
error: 'a valid email',
),
'name': isString(),
'roles': listEach(isString()).nullable(),
});
final result = apiUser.validate(apiJson);
if (result.isNotValid) log('invalid user: $result');
Validate runtime config and feature flags
final config = eskema({
'featureX': isBool(),
'theme': isOneOf(['light', 'dark']),
'retry': all([isInt(), isGte(0)]),
'allowedHosts': listEach(isString()).nullable(),
});
final isConfigValid = config.isValid(configMap);
assert(isConfigValid, 'Invalid config: $cfgRes');
More
Libraries
- eskema
- Eskema is a small, composable runtime validation library for Dart. It helps you validate dynamic values (JSON, Maps, Lists, primitives) with readable validators and clear error messages.
- extensions
- result
- util
- validator
- validators