{"id":16632255,"url":"https://github.com/benmccallum/fairybread","last_synced_at":"2025-05-15T18:10:48.256Z","repository":{"id":38242769,"uuid":"273927303","full_name":"benmccallum/fairybread","owner":"benmccallum","description":"Input validation for HotChocolate","archived":false,"fork":false,"pushed_at":"2024-11-30T01:06:16.000Z","size":354,"stargazers_count":120,"open_issues_count":9,"forks_count":13,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-08T00:36:31.716Z","etag":null,"topics":["fluentvalidation","hotchocolate","validation"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benmccallum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://www.buymeacoffee.com/benmccallum"]}},"created_at":"2020-06-21T15:04:51.000Z","updated_at":"2025-04-05T15:41:05.000Z","dependencies_parsed_at":"2023-09-25T02:43:34.802Z","dependency_job_id":"a7277732-89c3-4c1b-bb89-2ff3c005a87d","html_url":"https://github.com/benmccallum/fairybread","commit_stats":{"total_commits":179,"total_committers":8,"mean_commits":22.375,"dds":"0.12290502793296088","last_synced_commit":"bc36a550fca7910b524984dd786cfc73d5b311ff"},"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmccallum%2Ffairybread","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmccallum%2Ffairybread/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmccallum%2Ffairybread/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmccallum%2Ffairybread/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benmccallum","download_url":"https://codeload.github.com/benmccallum/fairybread/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254394724,"owners_count":22063984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fluentvalidation","hotchocolate","validation"],"created_at":"2024-10-12T04:59:33.846Z","updated_at":"2025-05-15T18:10:48.197Z","avatar_url":"https://github.com/benmccallum.png","language":"C#","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg alt=\"fairybread\" src=\"logo.svg\" height=\"200px\"\u003e\n  \u003cp\u003e\n    Input validation for Hot Chocolate.\n  \u003c/p\u003e\n  \u003cp\u003e\n\t  \u003ca href=\"https://github.com/benmccallum/fairybread/releases\"\u003e\u003cimg alt=\"GitHub release\" src=\"https://img.shields.io/github/release/benmccallum/fairybread.svg\"\u003e\u003c/a\u003e\n\t  \u003ca href=\"https://www.nuget.org/packages/FairyBread\"\u003e\u003cimg alt=\"Nuget version\" src=\"https://img.shields.io/nuget/v/FairyBread\"\u003e\u003c/a\u003e\n\t  \u003ca href=\"https://www.nuget.org/packages/FairyBread\"\u003e\u003cimg alt=\"NuGet downloads\" src=\"https://img.shields.io/nuget/dt/FairyBread\"\u003e\u003c/a\u003e\t  \n      \u003ca href=\"https://codecov.io/gh/benmccallum/FairyBread\"\u003e\n        \u003cimg src=\"https://codecov.io/gh/benmccallum/FairyBread/branch/main/graph/badge.svg?token=HB3O7GR51M\"/\u003e\n      \u003c/a\u003e    \n  \u003c/p\u003e\n\u003c/div\u003e\n\n## Getting started\n\nInstall a [compatible version](#Compatibility).\n\n```bash\ndotnet add package FairyBread\n```\n\nConfigure services.\n\n```csharp\n// Add your FluentValidation validators\n// (note: this will add all validators in the assembly that contains `CreateUserInputValidator`)\nservices.AddValidatorsFromAssemblyContaining\u003cCreateUserInputValidator\u003e();\n\n// Add FairyBread\nservices\n    .AddGraphQL()\n    .AddFairyBread();\n```\n\nConfigure [FluentValidation](https://github.com/FluentValidation/FluentValidation) validators on your input types.\n\n\n```csharp\npublic class CreateUserInput { ... }\n\npublic class CreateUserInputValidator : AbstractValidator\u003cCreateUserInput\u003e { ... }\n\n// An example GraphQL field in Hot Chocolate\npublic Task CreateUser(CreateUserInput userInput) { ... }\n```\n\n### How validation errors will be handled\n\nBy default, errors will be written out into the GraphQL execution result in the `Errors` property with one error being reported per failure on a field.\nYou can change this behaviour by implementing your own `IValidationErrorsHandler`.\n\n### Implicit vs explicit configuration\n\nFairyBread opts for an implicit approach to validation by default, similar to how\n[FluentValidation.AspNetCore](https://docs.fluentvalidation.net/en/latest/aspnet.html#asp-net-core)\nbehaves. Simply create a validator for a certain input object type and arguments of that type will be validated.\nAnd you don't need to worry about a middleware performance penalty, as FairyBread (since v7.1) only adds the validation\nfield middleware where needed.\n\nThere are some cases though where explicitness is either required or useful, so you can do that too.\n\nFor example, if you've got a field argument that's a scalar type (e.g. not an input type), like an `int`, creating\na validator targeting `int` would mean every top-level `int` argument across your schema would be implicitly validated, which wouldn't make sense.\nInstead, annotate the validator by having it inherit `IExplicitUsageOnlyValidator` and then explicitly setup it up on the argument (see below).\n\nAnnotation API:\n\n  * `[Validate(typeof(FooValidator)]` - explicitly add this validator for the argument\n  * `[DontValidate]` - don't validate this argument at all\n  * `[DontImplicitlyValidate]` - disable implicit validators for the argument\n\nFluent API:\n\n  * `.Argument(\"foo\").ValidateWith\u003cFooValidator\u003e()`\n  * `.DontValidate()`\n  * `.DontImplicitlyValidate()`\n\n### Dealing with multi-threaded execution issues\n\nGraphQL 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 this is the HTTP Request).\n\nWith FairyBread, you might need to do this if one of your validators uses a `DbContext` and could operate on the scoped DbContext at the same time as another part of the application. GraphQL mutation resolvers are executed serially, so typically you wouldn't encounter this issue for mutations. It's more likely you could encounter it for Query resolvers which can run in parallel.\n\nGood news is, it's as easy as marking your validator with `IRequiresOwnScopeValidator` and we'll take care of the rest.\n\n```csharp\npublic class SomeInputValidator\n    : AbstractValidator\u003cSomeInput\u003e\n    , IRequiresOwnScopeValidator\n{\n    // db will be a unique instance for this validation operation\n    public SomeInputValidator(SomeDbContext db) { ... } \n}\n```\n\n### Using MediatR?\n\nIf you want to let MediatR fire validation, you can set up:\n* FairyBread to skip validating `MediatR.IRequest` arguments, \n* your MediatR pipeline to validate them and throw a `ValidationException`, and\n* an `IErrorFilter`(in Hot Chocolate) to handle it using `FairyBread.DefaultValidationErrorsHandler` to report the errors.\n\n### Where to next?\n\nFor more examples, please see the tests.\n\n## Customization\n\nFairyBread was built with customization in mind.\n\nYou can tweak the default settings as needed:\n\n```csharp\nservices.AddFairyBread(options =\u003e\n{\n    options.ShouldValidateArgument = (objTypeDef, fieldTypeDef, argTypeDef) =\u003e ...;\n    options.ThrowIfNoValidatorsFound = true/false;\n});\n```\n\nYou can completely swap in your own concrete implementations of bits with DI.\nThese could be based on the default implementations whose methods can be overridden.\n\nSee \u003ca href=\"src/FairyBread.Tests/CustomizationTests.cs\"\u003eCustomizationTests.cs\u003c/a\u003e.\n\n## Backlog\n\nSee issues.\n\n## Compatibility\n\nFairyBread depends on [HotChocolate.Execution](https://www.nuget.org/packages/HotChocolate.Execution)\nwhich can bring breaking changes from time to time and require a major bump our end. This is also the case\nfor FluentValidation.\nCompatibility is listed below. \n\nNote, these are minimum versions, for instance, v12.0.1 through to 12.3.x of Hot Chocolate are supported by FairyBread v8.x.x.\n\nWe strive to match Hot Chocolate's supported .NET target frameworks, though this might not always be possible.\n\n| HotChocolate | FluentValidation | FairyBread | FairyBread docs |\n| ------------ | ---------------- | ---------- | --------------- |\n|     v14.0.0 |              v10 |         v11 | right here |\n|     v13.0.0 |              v10 |         v10 | [/v10/main](https://github.com/benmccallum/fairybread/tree/v10/main) branch |\n|     v12.4.0* |              v10 |         v9 | [/v9/main](https://github.com/benmccallum/fairybread/tree/v9/main) branch |\n|      v12.0.1 |              v10 |         v8 | [/v8/main](https://github.com/benmccallum/fairybread/tree/v8/main) branch |\n|      v11.0.9 |              v10 |         v7 | [/v7/main](https://github.com/benmccallum/fairybread/tree/v7/main) branch |\n|      v11.0.9 |              v10 |         v6 | [/v6/main](https://github.com/benmccallum/fairybread/tree/v6/main) branch |\n|      v11.0.9 |               v9 |         v5 | [/v5/main](https://github.com/benmccallum/fairybread/tree/v5/main) branch |\n|     v11.0.9* |               v9 |     v4.1.1 | [/v4/main](https://github.com/benmccallum/fairybread/tree/v4/main) branch |\n|          v11 |               v9 |         v4 | [/v4/main](https://github.com/benmccallum/fairybread/tree/v4/main) branch |\n|          v11 |               v9 |         v3 | [/v3/main](https://github.com/benmccallum/fairybread/tree/v3/main) branch |\n|          v11 |               v8 |         v2 | [/v2/main](https://github.com/benmccallum/fairybread/tree/v2/main) branch |\n|          v10 |               v8 |         v1 | [/v1/main](https://github.com/benmccallum/fairybread/tree/v1/main) branch |\n\n\\* Denotes unexpected binary incompatibility / breaking change in Hot Chocolate\n\n## What the heck is a fairy bread?\n\nA (bizarre) Australian food served at children's parties. Since I'm Australian and HotChocolate has a lot of \nproject names with sweet tendencies, this seemed like a fun name for the project.\n\nAccording to [wikipedia](https://en.wikipedia.org/wiki/Fairy_bread):\n\u003e \"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.\"\n","funding_links":["https://www.buymeacoffee.com/benmccallum"],"categories":["others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenmccallum%2Ffairybread","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenmccallum%2Ffairybread","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenmccallum%2Ffairybread/lists"}