AppAny.HotChocolate.FluentValidation
AppAny.HotChocolate.FluentValidation copied to clipboard
Input field HotChocolate GraphQL + FluentValidation integration
AppAny.HotChocolate.FluentValidation
Feature-rich, but simple, fast and memory efficient input field HotChocolate + FluentValidation integration ⚡️
🔧 Installation
$> dotnet add package AppAny.HotChocolate.FluentValidation
💡 Features
🚩 You don't pay for validation middleware if the field has no validatable inputs
🚩 You are not validating, and even trying to validate empty or not marked as validatable inputs
🚩 Most of extensibility points are just composable delegates
🚩 Fine-tuning of validation for each field: conditional validation skipping, multiple validators or error mappers per input
🚩 Strongly typed ValidationStrategy<T> support
🚩 First-class attribute-based approach support
🎨 Usage
✅ Add FluentValidation validator
public class ExampleInput
{
public string Example { get; set; }
}
public class ExampleInputValidator : AbstractValidator<ExampleInput>
{
public ExampleInputValidator()
{
RuleFor(input => input.ExampleProperty)
.NotEmpty()
.WithMessage("Property is empty");
}
}
✅ Configure HotChocolate + FluentValidation integration
# Since 10.2.0 https://github.com/FluentValidation/FluentValidation/releases/tag/10.2.0
services.AddFluentValidation();
services.AddGraphQLServer()
.AddFluentValidation();
descriptor.Field(x => x.Example(default!))
// Explicit over implicit preferred
// You have to add .UseFluentValidation()/attribute to all arguments requiring validation
.Argument("input", argument => argument.UseFluentValidation());
... Example([UseFluentValidation] ExampleInput input) { ... }
✅ Extend and customize
services.AddGraphQLServer()
.AddFluentValidation(options =>
{
options.SkipValidation(...)
.UseErrorMapper(...)
.UseInputValidators(...);
});
descriptor.Field(x => x.Example(default!))
.Argument("input", argument => argument.UseFluentValidation(options =>
{
options.SkipValidation(...)
.UseErrorMapper(...)
.UseInputValidators(...)
.UseValidationStrategy(...)
.UseValidator<ExampleInputValidator>()
.UseValidator<ExampleInput, ExampleInputValidator>()
.UseValidator<ExampleInput, ExampleInputValidator>(strategy =>
{
strategy.IncludeProperties(input => input.ExampleProperty);
// ...
});
}));
... Example([UseFluentValidation, UseValidator((typeof(ExampleInputValidator))] ExampleInput input) { ... }
📝 Docs
- 📄 Abstractions
- 📄 Defaults
- 📄 Examples
- 📄 Error mappers
- 📄 Validation strategies
- 📄 Input validators
- 📄 Root validator segregation
- 📄 Argument level overrides
- 📄 Attribute-based approach
♿️ Benchmarks 🚀
🚧 I swear I will check correctness, run these benchmarks on my own environment and only after that I will make conclusions 🚧
Breaking changes
- From 0.6.x to 0.7.x
- Default input validator throws
InvalidOperationExceptionif argument has[UseFluentValidation], but no validator registered inIServiceCollection
- Default input validator throws