AppAny.HotChocolate.FluentValidation
January 10, 2023 ยท View on GitHub
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 ๐
Breaking changes
- From 0.10.x to 0.11.x
- Replace
ValidationDefaults.Interceptorsstatic class withValidationDefaults.Interceptorproperty - Replace
ValidationInterceptorsstatic class withValidationInterceptor : TypeInterceptorclass
- Replace
- From 0.9.x to 0.10.x
- Update HC version to 13 preview
- 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