Skip to content

appany/AppAny.HotChocolate.FluentValidation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Nov 26, 2024
81f34f6 Β· Nov 26, 2024
Mar 5, 2022
Jan 5, 2021
May 20, 2021
Nov 26, 2024
Nov 26, 2024
Mar 26, 2021
Jan 14, 2021
Jan 7, 2021
Feb 8, 2021
Jan 5, 2021
Jan 10, 2023

Repository files navigation

AppAny.HotChocolate.FluentValidation

License Nuget Downloads Tests codecov

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

♿️ 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.10.x to 0.11.x
    • Replace ValidationDefaults.Interceptors static class with ValidationDefaults.Interceptor property
    • Replace ValidationInterceptors static class with ValidationInterceptor : TypeInterceptor class
  • 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 InvalidOperationException if argument has [UseFluentValidation], but no validator registered in IServiceCollection