Install a compatible version.
dotnet add package FairyBread
Configure services.
// Add your FluentValidation validators
services.AddValidatorsFromAssemblyContaining<CreateUserInputValidator>();
// Add FairyBread
services
.AddGraphQL()
.AddFairyBread();
Configure FluentValidation validators on your input types.
public class UserInput { ... }
public class UserInputValidator : AbstractValidator<CreateUserInput> { ... }
// An example GraphQL field in HotChocolate
public Task CreateUser(CreateUserInput userInput) { ... }
By default, FairyBread will validate any argument that:
- is an object type in a mutation operation,
- is manually opted-in at the field level with:
[Validate]
on the resolver method argument in pure code first.UseValidation()
on the argument definition in code first
- is manually opted-in at the input type level with:
[Validate]
on the CLR backing type in pure code first.UseValidation()
on the InputObjectType descriptor in code first
Note: by default, query operations are notably excluded. This is mostly for performance reasons.
If the default doesn't suit you, you can always change it by configuring IFairyBreadOptions.ShouldValidate
. See Customization.
Some of the default implementations are defined publicly on the default options class so they can be re-used and composed to form your own implementation.
Errors will be written out into the GraphQL execution result in the Errors
property with one error being reported per failure on a field.
GraphQL resolvers are inherently multi-threaded; as such, you can run into issues injecting things like an EntityFramework DbContext
into a field resolver (or something it uses) which doesn't allow muli-thread usage. One solution for this is to resolve DbContext
into its own "scope", rather than the default scope (for an ASP.NET Core application is tied to the HTTP Request).
With FairyBread, you might need to do this if one of your validators uses a DbContext
(say to check if a username already exists on a create user mutation). Good news is, it's as easy as marking your validator with IRequiresOwnScopeValidator
and we'll take care of the rest.
public class UserInputValidator : AbstractValidator<UserInput>, IRequiresOwnScopeValidator
{
// db will be a unique instance for this validation operation
public UserInputValidator(SomeDbContext db) { ... }
}
If you want to let MediatR fire validation, you can set up:
- FairyBread to skip validating
MediatR.IRequest
arguments, - your MediatR pipeline to validate them and throw a
ValidationException
, and - an
IErrorFilter
(in HotChocolate) to handle it handles usingFairyBread.DefaultValidationErrorsHandler
to report the errors.
For more examples, please see the tests.
FairyBread was built with customization in mind. At configuration time, you can tweak the default settings as needed:
services.AddFairyBread(options =>
{
options.ShouldValidate = (ctx, arg) => ...;
options.ThrowIfNoValidatorsFound = true/false;
});
Or, you can completely swap in your own options, validator provider, validation errors result handler and so on to get the functionality you need by simply adding your own implementation of the relevant interface before adding FairyBread. You can even subclass a default implementation, register it and override singular methods if that makes life easier.
Check out CustomizationTests.cs for complete examples.
See issues.
FairyBread depends on HotChocolate.Execution which can bring breaking changes from time to time and require a major bump our end. This is also the case for FluentValidation. Compatibility is listed below.
We strive to match HotChocolate's supported .NET target frameworks, though this might not always be possible.
HotChocolate | FluentValidation | FairyBread | FairyBread docs |
---|---|---|---|
v10 | v8 | v1 | /v1/main branch |
v11 | v8 | v2 | /v2/main branch |
v11 | v9 | v3 | /v3/main branch |
v11 | v9 | v4 | /v4/main branch |
v11.0.9* | v9 | v4.1.1 | /v4/main branch |
v11.0.9 | v9 | v5 | /v5/main branch |
v11.0.9 | v10 | v6 | right here |
- Unexpected binary incompatibility / breaking change in HotChocolate
A (bizarre) Australian food served at children's parties. Since I'm Australian and HotChocolate has a lot of project names with sweet tendencies, this seemed like a fun name for the project.
According to wikipedia:
"Fairy bread is sliced white bread spread with butter or margarine and covered with sprinkles or "hundreds and thousands", served at children's parties in Australia and New Zealand. It is typically cut into triangles."