diff --git a/website/src/docs/docs.json b/website/src/docs/docs.json
index 8eb64644bfa..5ed445238e1 100644
--- a/website/src/docs/docs.json
+++ b/website/src/docs/docs.json
@@ -6,6 +6,336 @@
"versions": [
{
"path": "",
+ "title": "v13",
+ "items": [
+ {
+ "path": "index",
+ "title": "Introduction"
+ },
+ {
+ "path": "get-started",
+ "title": "Get Started"
+ },
+ {
+ "path": "defining-a-schema",
+ "title": "Defining a schema",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "queries",
+ "title": "Queries"
+ },
+ {
+ "path": "mutations",
+ "title": "Mutations"
+ },
+ {
+ "path": "subscriptions",
+ "title": "Subscriptions"
+ },
+ {
+ "path": "object-types",
+ "title": "Object Types"
+ },
+ {
+ "path": "scalars",
+ "title": "Scalars"
+ },
+ {
+ "path": "arguments",
+ "title": "Arguments"
+ },
+ {
+ "path": "input-object-types",
+ "title": "Input Object Types"
+ },
+ {
+ "path": "lists",
+ "title": "Lists"
+ },
+ {
+ "path": "non-null",
+ "title": "Non-Null"
+ },
+ {
+ "path": "enums",
+ "title": "Enums"
+ },
+ {
+ "path": "interfaces",
+ "title": "Interfaces"
+ },
+ {
+ "path": "unions",
+ "title": "Unions"
+ },
+ {
+ "path": "extending-types",
+ "title": "Extending Types"
+ },
+ {
+ "path": "directives",
+ "title": "Directives"
+ },
+ {
+ "path": "documentation",
+ "title": "Documentation"
+ },
+ {
+ "path": "versioning",
+ "title": "Versioning"
+ },
+ {
+ "path": "relay",
+ "title": "Relay"
+ }
+ ]
+ },
+ {
+ "path": "fetching-data",
+ "title": "Fetching data",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "resolvers",
+ "title": "Resolvers"
+ },
+ {
+ "path": "fetching-from-databases",
+ "title": "Fetching from Databases"
+ },
+ {
+ "path": "fetching-from-rest",
+ "title": "Fetching from REST"
+ },
+ {
+ "path": "dataloader",
+ "title": "DataLoader"
+ },
+ {
+ "path": "pagination",
+ "title": "Pagination"
+ },
+ {
+ "path": "filtering",
+ "title": "Filtering"
+ },
+ {
+ "path": "sorting",
+ "title": "Sorting"
+ },
+ {
+ "path": "projections",
+ "title": "Projections"
+ }
+ ]
+ },
+ {
+ "path": "execution-engine",
+ "title": "Execution Engine",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "field-middleware",
+ "title": "Field middleware"
+ }
+ ]
+ },
+ {
+ "path": "integrations",
+ "title": "Integrations",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "entity-framework",
+ "title": "Entity Framework"
+ },
+ {
+ "path": "mongodb",
+ "title": "MongoDB"
+ },
+ {
+ "path": "neo4j",
+ "title": "Neo4J"
+ },
+ {
+ "path": "spatial-data",
+ "title": "Spatial Data"
+ }
+ ]
+ },
+ {
+ "path": "server",
+ "title": "Server",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "endpoints",
+ "title": "Endpoints"
+ },
+ {
+ "path": "dependency-injection",
+ "title": "Dependency Injection"
+ },
+ {
+ "path": "interceptors",
+ "title": "Interceptors"
+ },
+ {
+ "path": "global-state",
+ "title": "Global State"
+ },
+ {
+ "path": "introspection",
+ "title": "Introspection"
+ },
+ {
+ "path": "files",
+ "title": "Files"
+ },
+ {
+ "path": "instrumentation",
+ "title": "Instrumentation"
+ }
+ ]
+ },
+ {
+ "path": "distributed-schema",
+ "title": "Distributed Schemas",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "schema-stitching",
+ "title": "Schema Stitching"
+ },
+ {
+ "path": "schema-federations",
+ "title": "Schema Federations"
+ },
+ {
+ "path": "schema-configuration",
+ "title": "Schema Configuration"
+ }
+ ]
+ },
+ {
+ "path": "performance",
+ "title": "Performance",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "persisted-queries",
+ "title": "Persisted queries"
+ },
+ {
+ "path": "automatic-persisted-queries",
+ "title": "Automatic persisted queries"
+ }
+ ]
+ },
+ {
+ "path": "security",
+ "title": "Security",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "authentication",
+ "title": "Authentication"
+ },
+ {
+ "path": "authorization",
+ "title": "Authorization"
+ },
+ {
+ "path": "operation-complexity",
+ "title": "Operation Complexity"
+ }
+ ]
+ },
+ {
+ "path": "api-reference",
+ "title": "API Reference",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "custom-attributes",
+ "title": "Custom Attributes"
+ },
+ {
+ "path": "language",
+ "title": "Language"
+ },
+ {
+ "path": "extending-filtering",
+ "title": "Extending Filtering"
+ },
+ {
+ "path": "visitors",
+ "title": "Visitors"
+ },
+ {
+ "path": "aspnetcore",
+ "title": "ASP.NET Core"
+ },
+ {
+ "path": "apollo-federation",
+ "title": "Apollo Federation"
+ },
+ {
+ "path": "executable",
+ "title": "Executable"
+ }
+ ]
+ },
+ {
+ "path": "migrating",
+ "title": "Migrating",
+ "items": [
+ {
+ "path": "migrate-from-12-to-13",
+ "title": "Migrate from 12 to 13"
+ },
+ {
+ "path": "migrate-from-11-to-12",
+ "title": "Migrate from 11 to 12"
+ },
+ {
+ "path": "migrate-from-10-to-11",
+ "title": "Migrate from 10.5 to 11.0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "path": "v12",
"title": "v12",
"items": [
{
diff --git a/website/src/docs/hotchocolate/api-reference/migrate-from-10-to-11.md b/website/src/docs/hotchocolate/migrating/migrate-from-10-to-11.md
similarity index 100%
rename from website/src/docs/hotchocolate/api-reference/migrate-from-10-to-11.md
rename to website/src/docs/hotchocolate/migrating/migrate-from-10-to-11.md
diff --git a/website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md b/website/src/docs/hotchocolate/migrating/migrate-from-11-to-12.md
similarity index 100%
rename from website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md
rename to website/src/docs/hotchocolate/migrating/migrate-from-11-to-12.md
diff --git a/website/src/docs/hotchocolate/migrating/migrate-from-12-to-13.md b/website/src/docs/hotchocolate/migrating/migrate-from-12-to-13.md
new file mode 100644
index 00000000000..3514ef63037
--- /dev/null
+++ b/website/src/docs/hotchocolate/migrating/migrate-from-12-to-13.md
@@ -0,0 +1,3 @@
+---
+title: Migrate from Hot Chocolate GraphQL server 12 to 13
+---
diff --git a/website/src/docs/hotchocolate/v12/api-reference/apollo-federation.md b/website/src/docs/hotchocolate/v12/api-reference/apollo-federation.md
new file mode 100644
index 00000000000..8df45b2c14b
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/apollo-federation.md
@@ -0,0 +1,7 @@
+---
+title: Apollo Federation Subgraph Support
+---
+
+> Note: Apollo Federation Support is coming with Hot Chocolate 12.6
+
+## Example subgraphs
diff --git a/website/src/docs/hotchocolate/v12/api-reference/aspnetcore.md b/website/src/docs/hotchocolate/v12/api-reference/aspnetcore.md
new file mode 100644
index 00000000000..05dfa135d85
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/aspnetcore.md
@@ -0,0 +1,394 @@
+---
+title: ASP.NET Core
+---
+
+Hot Chocolate comes with integration to the ASP.NET Core endpoints API. The middleware implementation follows the current GraphQL over HTTP Spec.
+
+```csharp
+public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+{
+ app.UseRouting()
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGraphQL();
+ });
+}
+```
+
+# GraphQL over HTTP Spec
+
+The following GraphQL requests follow the current GraphQL over HTTP spec draft.
+
+If no path is specified, the GraphQL middleware will follow the spec recommendation to map the endpoint to `/graphql`.
+
+`http://example.com/graphql`
+
+`http://product.example.com/graphql`
+
+`http://example.com/product/graphql`
+
+## GraphQL HTTP POST requests
+
+The GraphQL HTTP POST request is the most commonly used variant for GraphQL requests over HTTP and is specified [here](https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#post).
+
+**request:**
+
+```http
+POST /graphql
+HOST: foo.example
+Content-Type: application/json
+
+{
+ "query": "query($id: ID!){user(id:$id){name}}",
+ "variables": { "id": "QVBJcy5ndXJ1" }
+}
+```
+
+**response:**
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "data": {
+ "user": {
+ "name": "Jon Doe"
+ }
+ }
+}
+```
+
+## GraphQL HTTP GET request
+
+GraphQL can also be served through an HTTP GET request. You have the same options as the HTTP POST request, just that the request properties are provided as query parameters. GraphQL HTTP GET requests can be a good choice if you are looking to cache GraphQL requests.
+
+For example, if we wanted to execute the following GraphQL query:
+
+```graphql
+query ($id: ID!) {
+ user(id: $id) {
+ name
+ }
+}
+```
+
+With the following query variables:
+
+```json
+{
+ "id": "QVBJcy5ndXJ1"
+}
+```
+
+This request could be sent via an HTTP GET as follows:
+
+**request:**
+
+```http
+GET /graphql?query=query(%24id%3A%20ID!)%7Buser(id%3A%24id)%7Bname%7D%7D&variables=%7B%22id%22%3A%22QVBJcy5ndXJ1%22%7D`
+HOST: foo.example
+```
+
+**response:**
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "data": {
+ "user": {
+ "name": "Jon Doe"
+ }
+ }
+}
+```
+
+> Note: {query} and {operationName} parameters are encoded as raw strings in the query component. Therefore if the query string contained operationName=null then it should be interpreted as the {operationName} being the string "null". If a literal null is desired, the parameter (e.g. {operationName}) should be omitted.
+
+The GraphQL HTTP GET request is specified [here](https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#get).
+
+By default, Hot Chocolate will only serve query operations when HTTP GET requests are used. You can change this default by specifying the GraphQL server options.
+
+```csharp
+public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+{
+ app.UseRouting()
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints
+ .MapGraphQL()
+ .WithOptions(new GraphQLServerOptions
+ {
+ AllowedGetOperations = AllowedGetOperations.QueryAndMutation
+ });
+ });
+}
+```
+
+You can also entirely deactivate HTTP GET request handling.
+
+```csharp
+public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+{
+ app.UseRouting()
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints
+ .MapGraphQL()
+ .WithOptions(new GraphQLServerOptions
+ {
+ EnableGetRequests = false
+ });
+ });
+}
+```
+
+## Incremental Delivery over HTTP
+
+The Hot Chocolate GraphQL server supports incremental delivery over HTTP, which essentially uses HTTP chunked transfer encoding combined with the [specification of multipart content defined by the W3 in rfc1341](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html).
+
+The incremental delivery is at the moment at the RFC stage and is specified [here](https://github.com/graphql/graphql-over-http/blob/master/rfcs/IncrementalDelivery.md).
+
+Incremental delivery is used with `@defer`, `@stream`, and with request batching.
+
+# Additional Requests
+
+Apart from the requests defined by the GraphQL over HTTP spec, Hot Chocolate allows you to batch requests, download the GraphQL SDL, and many more things.
+
+> Many of the request types stated in this section are on their way into the GraphQL over HTTP spec, and we will update this document as the spec, and its RFCs change.
+
+## GraphQL Schema request
+
+Although you can access and query the schema definition through introspection, we support fetching the GraphQL schema SDL as a file. The GraphQL schema SDL is richer with more information and easier to read.
+
+**request:**
+
+```http
+GET /graphql?sdl
+HOST: foo.example
+```
+
+**response:**
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/graphql
+
+type Query {
+ hello: String!
+}
+```
+
+## GraphQL HTTP POST batching request
+
+We support two kinds of batching variants.
+
+The first variant to batch GraphQL requests is by sending in an array of GraphQL requests. Hot Chocolate will execute them in order.
+
+```http
+POST /graphql
+HOST: foo.example
+Content-Type: application/json
+
+[
+ {
+ # The query document.
+ "query": "query getHero { hero { name } }",
+
+ # The name of the operation that shall be executed.
+ "operationName": "getHero",
+
+ # A key under which a query document was saved on the server.
+ "id": "W5vrrAIypCbniaIYeroNnw==",
+
+ # The variable values for this request.
+ "variables": {
+ "a": 1,
+ "b": "abc"
+ },
+
+ # Custom properties that can be passed to the execution engine context data.
+ "extensions": {
+ "a": 1,
+ "b": "abc"
+ }
+ },
+ {
+ # The query document.
+ "query": "query getHero { hero { name } }",
+
+ # The name of the operation that shall be executed.
+ "operationName": "getHero",
+
+ # A key under which a query document was saved on the server.
+ "id": "W5vrrAIypCbniaIYeroNnw==",
+
+ # The variable values for this request.
+ "variables": {
+ "a": 1,
+ "b": "abc"
+ },
+
+ # Custom properties that can be passed to the execution engine context data.
+ "extensions": {
+ "a": 1,
+ "b": "abc"
+ }
+ },
+]
+```
+
+The second GraphQL batching variant is called operation batching, where you send in one GraphQL request document with multiple operations. The operation execution order is then specified as a query param.
+
+```http
+POST /graphql?batchOperations=[a,b]
+HOST: foo.example
+Content-Type: application/json
+
+{
+ # The query document.
+ "query": "query a { hero { name } } query b { hero { name } }",
+
+ # The name of the operation that shall be executed.
+ "operationName": "getHero",
+
+ # A key under which a query document was saved on the server.
+ "id": "W5vrrAIypCbniaIYeroNnw==",
+
+ # The variable values for this request.
+ "variables": {
+ "a": 1,
+ "b": "abc"
+ },
+
+ # Custom properties that can be passed to the execution engine context data.
+ "extensions": {
+ "a": 1,
+ "b": "abc"
+ }
+}
+```
+
+By default, the GraphQL server will use the **incremental delivery over HTTP** specification to write the stream results as soon as they are available. This means that depending on your client implementation; you can start using the results as they appear in order.
+
+The serialization defaults can be changed like the following:
+
+```csharp
+services.AddHttpResultSerializer(
+ batchSerialization: HttpResultSerialization.JsonArray,
+ deferSerialization: HttpResultSerialization.MultiPartChunked)
+```
+
+> More about batching can be found [here](/docs/hotchocolate/v10/execution-engine/batching).
+
+# Subscription Transport
+
+Subscriptions are by default delivered over WebSocket. We have implemented the [GraphQL over WebSocket Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) specified by Apollo.
+
+## Alternative Transport Protocols
+
+With version 11.1, we will add alternative transport protocols like the [new proposal for the GraphQL over HTTP spec](https://github.com/graphql/graphql-over-http/pull/140).
+
+Moreover, we are working on allowing this protocol to be used over SignalR, which gives more flexibility to use subscriptions.
+
+# Tooling
+
+The Hot Chocolate GraphQL server comes right out of the gate with excellent tooling. By default, we are mapping our GraphQL IDE Banana Cake Pop to the GraphQL endpoint. This means you just need to open your browser and navigate to the configured endpoint to send requests to your server, explore your schema, or build-up tests.
+
+![GraphQL IDE](../../../images/get-started-bcp-query.png)
+
+The GraphQL IDE can be disabled by specifying tool options:
+
+```csharp
+endpoints
+ .MapGraphQL()
+ .WithOptions(
+ new GraphQLServerOptions
+ {
+ Tool = { Enable = false }
+ }));
+```
+
+# Serialization
+
+The Hot Chocolate GraphQL server has abstracted the result serialization with the `IHttpResultSerializer` interface. The server uses the registered implementation to resolve the HTTP status code, the HTTP content type, and the serialized response from a GraphQL execution result.
+
+```csharp
+///
+/// This interface specifies how a GraphQL result is serialized to a HTTP response.
+///
+public interface IHttpResultSerializer
+{
+ ///
+ /// Gets the HTTP content type for the specified execution result.
+ ///
+ ///
+ /// The GraphQL execution result.
+ ///
+ ///
+ /// Returns a string representing the content type,
+ /// eg. "application/json; charset=utf-8".
+ ///
+ string GetContentType(IExecutionResult result);
+
+ ///
+ /// Gets the HTTP status code for the specified execution result.
+ ///
+ ///
+ /// The GraphQL execution result.
+ ///
+ ///
+ /// Returns the HTTP status code, eg. .
+ ///
+ HttpStatusCode GetStatusCode(IExecutionResult result);
+
+ ///
+ /// Serializes the specified execution result.
+ ///
+ ///
+ /// The GraphQL execution result.
+ ///
+ ///
+ /// The HTTP response stream.
+ ///
+ ///
+ /// The request cancellation token.
+ ///
+ ValueTask SerializeAsync(
+ IExecutionResult result,
+ Stream stream,
+ CancellationToken cancellationToken);
+}
+```
+
+We have a default implementation (`DefaultHttpResultSerializer`) that can be used to built custom logic on top of the original implementation to make extensibility easier. By default, we are using `System.Text.Json` to serialize GraphQL execution results to JSON.
+
+A custom implementation of the result serializer is registered like the following:
+
+```csharp
+services.AddHttpResultSerializer();
+```
+
+If you, for instance, wanted to add some special error code handling when some error happened during execution, you could implement this like the following:
+
+```csharp
+public class MyCustomHttpResultSerializer : DefaultHttpResultSerializer
+{
+ public override HttpStatusCode GetStatusCode(IExecutionResult result)
+ {
+ if (result is IQueryResult queryResult &&
+ queryResult.Errors?.Count > 0 &&
+ queryResult.Errors.Any(error => error.Code == "SOME_AUTH_ISSUE"))
+ {
+ return HttpStatusCode.Forbidden;
+ }
+
+ return base.GetStatusCode(result);
+ }
+}
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/custom-attributes.md b/website/src/docs/hotchocolate/v12/api-reference/custom-attributes.md
new file mode 100644
index 00000000000..aec989975fe
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/custom-attributes.md
@@ -0,0 +1,192 @@
+---
+title: "Custom Attributes"
+---
+
+Hot Chocolate allows to define a schema in various ways. When defining schemas with pure .NET types and custom attributes we need a way to access advanced features like custom field middleware that we have at our disposal with schema types.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.Strings).UsePaging();
+ }
+}
+```
+
+This is where descriptor attributes come in. Descriptor attributes allow us to package descriptor configurations into an attribute that can be used to decorate our .NET types. Descriptor attributes act like an interceptor into the configuration of the inferred schema type.
+
+# Built-In Attributes
+
+We have prepared the following set of built-in descriptor attributes.
+
+> ⚠️ **Note:** As middleware comprises the stages of a sequential _pipeline_, the ordering is important. The correct order to use is `UsePaging`, `UseFiltering`, `UseSorting`.
+
+## UsePagingAttribute
+
+The `UsePagingAttribute` allows us to use the paging middleware by annotating it to a property or method.
+
+```csharp
+public class Query
+{
+ [UsePaging]
+ public IQueryable GetFoos()
+ {
+ ...
+ }
+}
+```
+
+## UseFilteringAttribute
+
+The `UseFilteringAttribute` allows us to apply the filtering middleware to a property or method.
+
+```csharp
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetFoos()
+ {
+ ...
+ }
+}
+```
+
+> ⚠️ **Note**: Be sure to install the `HotChocolate.Types.Filters` NuGet package.
+
+## UseSortingAttribute
+
+The `UseSortingAttribute` allows us to apply the sorting middleware to a property or method.
+
+```csharp
+public class Query
+{
+ [UseSorting]
+ public IQueryable GetFoos()
+ {
+ ...
+ }
+}
+```
+
+> ⚠️ **Note**: Be sure to install the `HotChocolate.Types.Sorting` NuGet package.
+
+## AuthorizeAttribute
+
+The `AuthorizeAttribute` allows to apply the authorize directives to a class, struct, interface, property or method. The attribute will only be applied if the inferred type is an object type.
+
+```csharp
+public class Query
+{
+ [Authorize(Policy = "MyPolicy")]
+ public IQueryable GetFoos()
+ {
+ ...
+ }
+}
+```
+
+# Attribute Chaining
+
+Attributes can by default be chained, meaning that the attributes are applied in order from the top one to the bottom one.
+
+The following code ...
+
+```csharp
+public class Query
+{
+ [UsePaging]
+ [UseFiltering]
+ [UseSorting]
+ public IQueryable GetFoos()
+ {
+ ...
+ }
+}
+```
+
+... would translate to:
+
+```csharp
+public class QueryType
+ : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.Foos)
+ .UsePaging>()
+ .UseFiltering()
+ .UseSorting();
+ }
+}
+```
+
+# Custom Descriptor Attributes
+
+It is super simple to create custom descriptor attributes and package complex functionality in simple to use attributes.
+
+```csharp
+public class SomeMiddlewareAttribute
+ : ObjectFieldDescriptorAttribute
+{
+ public override void OnConfigure(
+ IDescriptorContext context,
+ IObjectFieldDescriptor descriptor,
+ MemberInfo member)
+ {
+ descriptor.Use(next => context => ...);
+ }
+}
+```
+
+Within the `OnConfigure` method you can do what you actually would do in the `Configure` method of a type.
+
+But you also get some context information about where the configuration was applied to, like you get the member to which the attribute was applied to and you get the descriptor context.
+
+We have one descriptor base class for each first-class descriptor type.
+
+- EnumTypeDescriptorAttribute
+- EnumValueDescriptorAttribute
+- InputObjectTypeDescriptorAttribute
+- InputFieldDescriptorAttribute
+- InterfaceTypeDescriptorAttribute
+- InterfaceFieldDescriptorAttribute
+- ObjectTypeDescriptorAttribute
+- ObjectFieldDescriptorAttribute
+- UnionTypeDescriptorAttribute
+- ArgumentDescriptorAttribute
+
+All of these attribute base classes have already the allowed attribute targets applied. That means that we pre-configured the `ObjectFieldDescriptorAttribute` for instance to be only valid on methods and properties.
+
+If you want to build more complex attributes that can be applied to multiple targets like an interface type and an object type at the same time then you can use our `DescriptorAttribute` base class. This base class is not pre-configured and lets you probe for configuration types.
+
+```csharp
+[AttributeUsage(
+ AttributeTargets.Property | AttributeTargets.Method,
+ Inherited = true,
+ AllowMultiple = true)]
+public sealed class MyCustomAttribute : DescriptorAttribute
+{
+ protected override void TryConfigure(
+ IDescriptorContext context,
+ IDescriptor descriptor,
+ ICustomAttributeProvider element)
+ {
+ if(element is MemberInfo member)
+ {
+ switch(descriptor)
+ {
+ case IInterfaceFieldDescriptor interfaceField:
+ // do something ...
+ break;
+
+ case IObjectFieldDescriptor interfaceField:
+ // do something ...
+ break;
+ }
+ }
+ }
+}
+```
+
+It is simple to use these attributes. Just annotating a type or a property with an attribute will add the packaged functionality. The types can be used in conjunction with schema types or without.
diff --git a/website/src/docs/hotchocolate/v12/api-reference/custom-context-data.md b/website/src/docs/hotchocolate/v12/api-reference/custom-context-data.md
new file mode 100644
index 00000000000..b582d1b3885
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/custom-context-data.md
@@ -0,0 +1,93 @@
+---
+title: Custom Context Data
+---
+
+When implementing custom middleware, it can be useful to be able to store some custom state on the context. This could be to build up a cache or other state data. Hot Chocolate has two types of context stores that we can use.
+
+# Global Context Data
+
+The global context data is a thread-safe dictionary that is available though the `IQueryContext` and the `IResolverContext`. This means we are able to share context data between query middleware components and field middleware components.
+
+One common use case is to aggregate some state when the GraphQL request is created and use it in field middleware or in the resolver.
+
+In order to intercept the request creation we can add an `IQueryRequestInterceptor` to our services and there build up our custom state.
+
+```csharp
+services.AddQueryRequestInterceptor((ctx, builder, ct) =>
+{
+ builder.SetProperty("Foo", new Foo());
+ return Task.CompletedTask;
+});
+```
+
+We can access the initial provided data in a query middleware, field middleware or our resolver.
+
+Query Middleware Example:
+
+```csharp
+builder.Use(next => context =>
+{
+ // access data
+ var foo = (Foo)context.ContextData["Foo"];
+
+ // set new data
+ context.ContextData["Bar"] = new Bar();
+
+ return next.Invoke(context);
+});
+```
+
+Field Middleware Example:
+
+```csharp
+SchemaBuilder.New()
+ .Use(next => context =>
+ {
+ // access data
+ var foo = (Foo)context.ContextData["Foo"];
+
+ // set new data
+ context.ContextData["Bar"] = new Bar();
+
+ return next.Invoke(context);
+ })
+ .Create();
+```
+
+Resolver Example:
+
+```csharp
+public Task MyResolver([State("Foo")]Foo foo)
+{
+ ...
+}
+```
+
+# Scoped Context Data
+
+The scoped context data is a immutable dictionary and is only available through the `IResolverContext`.
+
+Scoped state allows us to aggregate state for our child field resolvers.
+
+Let's say we have the following query:
+
+```graphql
+{
+ a {
+ b {
+ c
+ }
+ }
+ d {
+ e {
+ f
+ }
+ }
+}
+```
+
+If the `a`-resolver would put something on the scoped context its sub-tree could access that data. This means, `b` and `c` could access the data but `d`, `e` and `f` would _NOT_ be able to access the data, their dictionary is still unchanged.
+
+```csharp
+context.ScopedContextData = context.ScopedContextData.SetItem("foo", "bar");
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/error-filter.md b/website/src/docs/hotchocolate/v12/api-reference/error-filter.md
new file mode 100644
index 00000000000..89cb7103774
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/error-filter.md
@@ -0,0 +1,80 @@
+---
+title: Error Filter
+---
+
+GraphQL errors in Hot Chocolate are passed to the query result by returning an instance of `IError` or an enumerable of `IError` in a field resolver.
+
+Moreover, you can throw a `QueryException` that will be be caught by the query engine and translated to a field error.
+
+One further way to raise an error are non-terminating field errors. This can be raised by using `IResolverContext.RaiseError`. So, with this you can provide a result and raise an error for your current field.
+
+> If you do want to log errors head over to our diagnostic source [documentation](/docs/hotchocolate/v10/execution-engine/instrumentation) and see how you can hook up your logging framework of choice to it.
+
+# Error Builder
+
+Since, errors can have a lot of properties depending on your case we have introduced a new error builder which provides a nice API without thousands of overloads.
+
+```csharp
+return ErrorBuilder.New()
+ .SetMessage("This is my error.")
+ .SetCode("FOO_BAR")
+ .Build();
+```
+
+# Exceptions
+
+If some other exception is thrown during the query execution, then the execution engine will create an instance of `IError` with the message **Unexpected Execution Error** and the actual exception assigned to the error. However, the exception details will not be serialized so by default the user will only see the error message **Unexpected Execution Error**.
+
+If you want to translate exceptions into errors with useful information then you can write an `IErrorFilter`.
+
+An error filter has to be registered with the execution builder or with your dependency injection.
+
+```csharp
+IQueryExecuter executer = schema.MakeExecutable(builder =>
+ builder.UseDefaultPipeline(options)
+ .AddErrorFilter());
+```
+
+OR
+
+```csharp
+services.AddErrorFilter();
+```
+
+It is also possible to just register the error filter as a delegate like the following.
+
+```csharp
+IQueryExecuter executer = schema.MakeExecutable(builder =>
+ builder.UseDefaultPipeline(options)
+ .AddErrorFilter(error =>
+ {
+ if (error.Exception is NullReferenceException)
+ {
+ return error.WithCode("NullRef");
+ }
+ return error;
+ }));
+```
+
+Since errors are immutable we have added some helper functions like `WithMessage`, `WithCode` and so on that create a new error with the desired properties. Moreover, you can create an error builder from an error and modify multiple properties and then rebuild the error object.
+
+```csharp
+return ErrorBuilder.FromError(error)
+ .SetMessage("This is my error.")
+ .SetCode("FOO_BAR")
+ .Build();
+```
+
+# Exception Details
+
+In order to automatically add exception details to your GraphQL error you can switch the execution option to include exception details. By default we will switch this on if the debugger is attached. You can overwrite the behavior by setting the option.
+
+```csharp
+SchemaBuilder.New()
+ ...
+ .Create()
+ .MakeExecutable(new QueryExecutionOptions
+ {
+ IncludeExceptionDetails = true
+ });
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/executable.md b/website/src/docs/hotchocolate/v12/api-reference/executable.md
new file mode 100644
index 00000000000..bad91256aa0
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/executable.md
@@ -0,0 +1,127 @@
+---
+title: Executable
+---
+
+The `IExecutable` and `IExecutable` interfaces are intended to be used by data providers.
+These interfaces can abstract any kind of data source.
+The data or domain layer can wrap data in an executable and pass it to the GraphQL layer.
+A GraphQL resolver that returns an `IExecutable` is recognized as a list.
+
+```csharp
+public class User
+{
+ public string Name { get; }
+}
+
+public interface IUserRepostiory
+{
+ public IExecutable FindAll();
+}
+
+public class Query
+{
+ public IExecutable GetUsers([Service] IUserRepostiory repo) =>
+ repo.FindAll();
+}
+```
+
+```sdl
+type Query {
+ users: [User!]!
+}
+```
+
+This abstraction can be used to completely decouple the GraphQL layer form the database-specific knowledge.
+
+Filtering, sorting, projections et al, can pick up the executable and apply logic to it. There is still
+a database-specific provider needed for these features, but it is opaque to the GraphQL layer.
+
+The `IExecutable` is known to the execution engine. The engine calls `ToListAsync`, `FirstOrDefault` or
+`SingleOrDefault` on the executable. The executable shall execute it in the most efficient way for the
+database.
+
+# API
+
+## Source
+
+```csharp
+ object Source { get; }
+```
+
+The source property stores the current state of the executable
+
+In the EnittyFramework executable this property holds the `IQueryable`. In the `MongoExecutable` it is the
+`DbSet` or the `IAggregateFluent`. `Source` is deliberately read-only. If you have a custom implementation
+of `IExecutable` and you want to set the `Source`, you should create a method that returns a new executable
+with the new source
+
+## ToListAsync
+
+```csharp
+ ValueTask ToListAsync(CancellationToken cancellationToken);
+```
+
+Should return a list of ``.
+
+## FirstOrDefault
+
+```csharp
+ ValueTask FirstOrDefault(CancellationToken cancellationToken);
+```
+
+Should return the first element of a sequence, or a default value if the sequence contains no elements.
+
+## SingleOrDefault
+
+```csharp
+ ValueTask SingleOrDefault(CancellationToken cancellationToken);
+```
+
+Should return the only element of a default value if no such element exists. This method
+should throw an exception if more than one element satisfies the condition.
+
+## Print
+
+```csharp
+string Print();
+```
+
+Prints the executable in its current state
+
+# Example
+
+```csharp
+public class EntityFrameworkExecutable : QueryableExecutable
+{
+ public IQueryable Source { get; }
+
+ object IExecutable.Source => Source;
+
+ public EntityFrameworkExecutable(IQueryable queryable) : base(queryable)
+ {
+ }
+
+ ///
+ /// Returns a new enumerable executable with the provided source
+ ///
+ /// The source that should be set
+ /// The new instance of an enumerable executable
+ public QueryableExecutable WithSource(IQueryable source)
+ {
+ return new QueryableExecutable(source);
+ }
+
+ public override async ValueTask ToListAsync(CancellationToken cancellationToken) =>
+ await Source.ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ public override async ValueTask FirstOrDefaultAsync(
+ CancellationToken cancellationToken) =>
+ await Source.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+
+ public override async ValueTask SingleOrDefaultAsync(
+ CancellationToken cancellationToken) =>
+ await Source.SingleOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+
+ public override string Print() => Source.ToQueryString();
+}
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/extending-filtering.md b/website/src/docs/hotchocolate/v12/api-reference/extending-filtering.md
new file mode 100644
index 00000000000..f0682dcd2df
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/extending-filtering.md
@@ -0,0 +1,419 @@
+---
+title: Extending Filtering
+---
+
+> **Work in progress**: This documentation is not yet complete.
+
+The `HotChocolate.Data` package works with all databases that support `IQueryable`. Included in the
+default settings, are all filter operations that work over `IQueryable` on all databases.
+Sometimes this is not enough. Some databases might not support `IQueryable`. Some other databases may have
+technology-specific operations (e.g. SQL Like). Filtering was designed with extensibility in mind.
+
+Filtering can be broken down into two basic parts. Schema building and execution. In schema building,
+the input types are created. In execution, the data passed by the user is analyzed and translated to a
+database query. Both parts can be configured over a convention.
+
+In theory, you are free to design the structure of filters as it suits you best.
+Usually, it makes sense to divide the structure into two parts. The _field_ and the _operation_.
+
+The query below returns all movies where the franchise is equal to "Star Wars". The _field_ `franchise` where the filter
+is applied to and the _operation_ equals (`eq`) that should operate on this field.
+
+```graphql
+{
+ movies(where: { franchise: { eq: "Star Wars" } }) {
+ name
+ }
+}
+```
+
+Fields can also form paths. In the query below there are two _fields_ `genre` and `totalMovieCount` and one operation equals
+`eq`
+
+```graphql
+{
+ movies(where: { genre: { totalMovieCount: { eq: 100 } } }) {
+ name
+ }
+}
+```
+
+The two queries above show the difference between _fields_ and _operations_ well. A field is always context-specific.
+Even when two fields have the same name, like the description of a movie and the description of a genre, they have different meanings.
+One field refers to the description of a movie and the other description refers to the description of a genre.
+Same name, different meanings. An operation on the other hand, has always the same meaning.
+The equals operation (`eq`) do always mean that the value of the selected field, should
+be equals to the value that was provided in the query.
+Operations can be applied in different contexts, but the operation itself, stays the same.
+The name of the operation should be consistent. There should only be one operation that checks for equality.
+This operation should always have the same name.
+
+With this in mind, we can have a deeper dive into filtering. Buckle up, this might get exciting.
+
+# How everything fits together
+
+At the core of the configuration API of filtering there sits a convention. The convention holds the whole
+configuration that filtering needs to create filter types and to translate them to the database.
+During schema creation, the schema builder asks the convention how the schema should look like.
+The convention defines the names and descriptions of types and fields and also what the type should be used for properties.
+The convention also defines what provider should be used to translate a GraphQL query to a database query.
+The provider is the only thing that is used after the schema is built.
+Every field or operation in a filter type has a handler annotated.
+During schema initialization, these handlers are bound, to the GraphQL fields. The provider can specify which handler should be bound to which field.
+During execution, the provider visits the incoming value node and executes the handler on the fields.
+This loose coupling allows defining the provider independently of the convention.
+
+# Filter Convention
+
+A filter convention is a dotnet class that has to implement the interface `IFilterConvention`.
+Instead of writing a convention completely new, it is recommended to extend the base convention `FilterConvention`
+This convention is also configurable with a fluent interface, so in most cases you can probably just use the descriptor API.
+
+## Descriptor
+
+Most of the capabilities of the descriptor are already documented under `Fetching Data -> Filtering`.
+If you have not done this already, it is now the right time to head over to [Filtering](/docs/hotchocolate/v12/fetching-data/filtering) and read the parts about the `FilterConventions`
+
+There are two things on this descriptor that are not documented in `Fetching Data`:
+
+### Operation
+
+```csharp
+ IFilterOperationConventionDescriptor Operation(int operationId);
+```
+
+Operations are configured globally. Each operation has a unique identifier. You can find the build-in identifiers in `DefaultFilterOperations`.
+This identifier is used in the `FilterInputType`'s to bind operations on a type. Filter operations can also be configured with a fluent interface.
+You can specify the name and the description of the operation. This configuration is applied to all operation fields a `FilterInputType` defines.
+
+```csharp
+conventionDescriptor
+ .Operation(DefaultFilterOperations.Equals)
+ .Name("equals")
+ .Description("Compares the value of the input to the value of the field");
+```
+
+With this configuration, all equals operations are now no longer names `eq` but `equals` and have a description.
+
+If you want to create your own operations, you have to choose an identifier.
+To make sure to not collide with the framework, choose a number that is higher than 1024.
+If you are a framework developer and want to create an extension for HotChocolate, talk to us.
+We can assign you a range of operations so you do not collide with the operations defined by users.
+
+You will need this identifier later, so it probably makes sense to store it somewhere on a class
+
+```csharp
+public static class CustomOperations
+{
+ public const int Like = 1025;
+}
+
+public static class CustomerFilterConventionExtensions
+{
+ public static IFilterConventionDescriptor AddInvariantComparison(
+ this IFilterConventionDescriptor conventionDescriptor) =>
+ conventionDescriptor
+ .Operation(CustomOperations.Like)
+ .Name("like");
+}
+```
+
+To apply this configuration to operations types, you can use the Configure method
+
+```csharp
+ conventionDescriptor.Configure(
+ x => x.Operation(CustomOperations.Like))
+```
+
+### Provider
+
+```csharp
+ IFilterConventionDescriptor Provider()
+ where TProvider : class, IFilterProvider;
+ IFilterConventionDescriptor Provider(TProvider provider)
+ where TProvider : class, IFilterProvider;
+ IFilterConventionDescriptor Provider(Type provider);
+```
+
+On the convention, you can also specify what provider should be used. For now you need just to know
+that you can configure the provider here. We will have a closer look at the provider later.
+
+```csharp
+conventionDescriptor.Provider();
+```
+
+## Custom Conventions
+
+Most of the time the descriptor API should satisfy your needs. It is recommended to build extensions
+based on the descriptor API, rather than creating a custom convention.
+However, if you want to have full control over naming and type creation, you can also override the methods
+you need on the `FilterConvention`.
+
+You can also override the configure method to have a (probably) familiar API experience.
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ desciptor.AddDefaults();
+ }
+
+ public override NameString GetTypeName(Type runtimeType) =>
+ base.GetTypeName(runtimeType) + "Suffix";
+}
+```
+
+# Providers
+
+Like the convention, a provider can be configured over a fluent interface.
+Every filter field or operation has a specific handler defined. The handler translates the operation to the database.
+These handlers are stored on the provider. After the schema is initialized, an interceptor visits the filter types and requests a handler from the provider.
+The handler is annotated directly on the field.
+The provider translates an incoming query into a database query by traversing an input object and executing the handlers on the fields.
+
+The output of a translation is always some kind of _filter definition_. In case, of `IQueryable` this is an expression.
+In case, of MongoDB this is a `FilterDefinition`. Provider, visitor context and handler, operate on and produce this _filter definition_.
+
+To inspect and analyze the input object, the provider uses a visitor.
+
+What a visitor is and how you can write you own visitor is explained [here](/docs/hotchocolate/v12/api-reference/visitors)
+
+Visitors are a powerful yet complex concept, we tried our best to abstract it away.
+For most cases, you will not need to create a custom visitor.
+
+## Provider Descriptor
+
+The descriptor of a provider is simple. It only has one method:
+
+```csharp
+ IFilterProviderDescriptor AddFieldHandler()
+ where TFieldHandler : IFilterFieldHandler;
+```
+
+With this method you can register field handlers on the provider.
+
+## Field Handler
+
+Every field or operation is annotated with an instance of a `FilterFieldHandler`. When the provider is asked for a handler for a field, it iterates sequentially through the list of existing field handlers and calls the `CanHandle` method.
+The first field handler that can handle the field, is annotated on the field.
+As the visitor traverses the input object, it calls `TryHandleEnter` as it enters the input field and `TryHandleLeave` as it leaves it.
+
+> A field handler supports constructor injection and is a singleton. Do not store data on the field handler. use the `context` of the visitor for state management.
+
+### CanHandle
+
+```csharp
+ bool CanHandle(
+ ITypeCompletionContext context,
+ IFilterInputTypeDefinition typeDefinition,
+ IFilterFieldDefinition fieldDefinition);
+```
+
+Tests if this field handler can handle a field. If it can handle the field it will be attached to it.
+
+### TryHandleEnter
+
+```csharp
+bool TryHandleEnter(
+ TContext context,
+ IFilterField field,
+ ObjectFieldNode node,
+ [NotNullWhen(true)] out ISyntaxVisitorAction? action);
+```
+
+This method is called when the visitor encounters a field.
+
+- `context` is the context of the visitor
+- `field` is the instance of the field that is currently visited
+- `node` is the field node of the input object. `node.Value` contains the value of the field.
+- `action` If `TryHandleEnter` returns true, the action is used for further processing by the visitor.
+
+### TryHandleLeave
+
+```csharp
+bool TryHandleLeave(
+ TContext context,
+ IFilterField field,
+ ObjectFieldNode node,
+ [NotNullWhen(true)] out ISyntaxVisitorAction? action);
+```
+
+This method is called when the visitor leave the field it previously entered.
+
+- `context` is the context of the visitor
+- `field` is the instance of the field that is currently visited
+- `node` is the field node of the input object. `node.Value` contains the value of the field.
+- `action` If `TryHandleLeave` returns true, the action is used for further processing by the visitor.
+
+## Filter Operation Handlers
+
+There is only one kind of field handler. To make it easier to handle operations, there also exists `FilterOperationHandler`, a more specific abstraction.
+You can override `TryHandleOperation` to handle operations.
+
+## The Context
+
+As the visitor and the field handlers are singletons, a context object is passed along with the traversation of input objects.
+Field handlers can push data on this context, to make it available for other handlers further down in the tree.
+
+The context contains `Types`, `Operations`, `Errors` and `Scopes`. It is very provider-specific what data you need to store in the context.
+In the case of the `IQueryable` provider, it also contains `RuntimeTypes` and knows if the source is `InMemory` or a database call.
+
+With `Scopes` it is possible to add multiple logical layers to a context. In the case of `IQuerable` this is needed, whenever a new closure starts
+
+```csharp
+// /------------------------ SCOPE 1 -----------------------------\
+// /----------- SCOPE 2 -------------\
+users.Where(x => x.Company.Addresses.Any(y => y.Street == "221B Baker Street"))
+```
+
+A filter statement that produces the expression above would look like this
+
+```graphql
+{
+ users(
+ where: {
+ company: { addresses: { any: { street: { eq: "221B Baker Street" } } } }
+ }
+ ) {
+ name
+ }
+}
+```
+
+A little simplified this is what happens during visitation:
+
+```graphql
+{
+ users(
+ # level[0] = []
+ # instance[0] = x
+ # Create SCOPE 1 with parameter x of type User
+ where: {
+ # Push property User.Company onto the scope
+ # instance[1] = x.Company
+ # level[1] = []
+ company: {
+ # Push property Company.Addresses onto the scope
+ # instance[2] x.Company.Addresses
+ # level[2] = []
+ addresses: {
+ # Create SCOPE 2 with parameter y of type Address
+ # instance[0] = y
+ # level[0] = []
+ any: {
+ # Push poperty Address.Street onto the scope
+ # instance[1] = y.Street
+ # level[1] = []
+ street: {
+ # Create and push the operation onto the scope
+ # instance[2] = y.Street
+ # level[2] = [y.Street == "221B Baker Street"]
+ eq: "221B Baker Street"
+ }
+ # Combine everything of the current level and pop the porperty street from the instance
+ # instance[1] = y.Street
+ # level[1] = [y.Street == "221B Baker Street"]
+ }
+ # Combine everything of the current level, create the any operation and exit SCOPE 2
+ # instance[2] = x.Company.Addresses
+ # level[2] = [x.Company.Addresses.Any(y => y.Street == "221B Baker Street")]
+ }
+ # Combine everything of the current level and pop the porperty street from the instance
+ # instance[1] = x.Company
+ # level[1] = [x.Company.Addresses.Any(y => y.Street == "221B Baker Street")]
+ }
+ # Combine everything of the current level and pop the porperty street from the instance
+ # instance[0] = x
+ # level[0] = [x.Company.Addresses.Any(y => y.Street == "221B Baker Street")]
+ }
+ ) {
+ name
+ }
+}
+```
+
+# Extending IQueryable
+
+The default filtering implementation uses `IQueryable` under the hood. You can customize the translation of queries by registering handlers on the `QueryableFilterProvider`.
+
+The following example creates a `StringOperationHandler` that supports case insensitive filtering:
+
+```csharp
+// The QueryableStringOperationHandler already has an implemenation of CanHandle
+// It checks if the field is declared in a string operation type and also checks if
+// the operation of this field uses the `Operation` specified in the override property further
+// below
+public class QueryableStringInvariantEqualsHandler : QueryableStringOperationHandler
+{
+ // For creating a expression tree we need the `MethodInfo` of the `ToLower` method of string
+ private static readonly MethodInfo _toLower = typeof(string)
+ .GetMethods()
+ .Single(
+ x => x.Name == nameof(string.ToLower) &&
+ x.GetParameters().Length == 0);
+
+ // This is used to match the handler to all `eq` fields
+ protected override int Operation => DefaultFilterOperations.Equals;
+
+ public override Expression HandleOperation(
+ QueryableFilterContext context,
+ IFilterOperationField field,
+ IValueNode value,
+ object parsedValue)
+ {
+ // We get the instance of the context. This is the expression path to the propert
+ // e.g. ~> y.Street
+ Expression property = context.GetInstance();
+
+ // the parsed value is what was specified in the query
+ // e.g. ~> eq: "221B Baker Street"
+ if (parsedValue is string str)
+ {
+ // Creates and returnes the operation
+ // e.g. ~> y.Street.ToLower() == "221b baker street"
+ return Expression.Equal(
+ Expression.Call(property, _toLower),
+ Expression.Constant(str.ToLower()));
+ }
+
+ // Something went wrong 😱
+ throw new InvalidOperationException();
+ }
+}
+```
+
+This operation handler can be registered on the convention:
+
+```csharp
+public class CustomFilteringConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor.AddDefaults();
+ descriptor.Provider(
+ new QueryableFilterProvider(
+ x => x
+ .AddDefaultFieldHandlers()
+ .AddFieldHandler()));
+ }
+}
+
+// and then
+services.AddGraphQLServer()
+ .AddFiltering();
+```
+
+To make this registration easier, Hot Chocolate also supports convention and provider extensions.
+Instead of creating a custom `FilterConvention`, you can also do the following:
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddFiltering()
+ .AddConvention(
+ new FilterConventionExtension(
+ x => x.AddProviderExtension(
+ new QueryableFilterProviderExtension(
+ y => y.AddFieldHandler()))));
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/filtering.md b/website/src/docs/hotchocolate/v12/api-reference/filtering.md
new file mode 100644
index 00000000000..57864585889
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/filtering.md
@@ -0,0 +1,2031 @@
+---
+title: Filtering
+---
+
+**What are filters?**
+
+With Hot Chocolate filters, you can expose complex filter objects through your GraphQL API that translates to native database queries.
+
+The default filter implementation translates filters to expression trees and applies these on `IQueryable`.
+
+# Overview
+
+Filters by default work on `IQueryable` but you can also easily customize them to use other interfaces.
+
+Hot Chocolate by default will inspect your .NET model and infer the possible filter operations from it.
+
+The following type would yield the following filter operations:
+
+```csharp
+public class Foo
+{
+ public string Bar { get; set; }
+}
+```
+
+```graphql
+input FooFilter {
+ bar: String
+ bar_contains: String
+ bar_ends_with: String
+ bar_in: [String]
+ bar_not: String
+ bar_not_contains: String
+ bar_not_ends_with: String
+ bar_not_in: [String]
+ bar_not_starts_with: String
+ bar_starts_with: String
+ AND: [FooFilter!]
+ OR: [FooFilter!]
+}
+```
+
+**So how can we get started with filters?**
+
+Getting started with filters is very easy, especially if you do not want to explicitly define filters or customize anything.
+
+Hot Chocolate will infer the filters directly from your .Net Model and then use a Middleware to apply filters to `IQueryable` or `IEnumerable` on execution.
+
+> ⚠️ **Note:** If you use more than middleware, keep in mind that **ORDER MATTERS**.
+
+> ⚠️ **Note:** Be sure to install the `HotChocolate.Types.Filters` NuGet package.
+
+In the following example, the person resolver returns the `IQueryable` representing the data source. The `IQueryable` represents a not executed database query on which Hot Chocolate can apply filters.
+
+**Code First**
+
+The next thing to note is the `UseFiltering` extension method which adds the filter argument to the field and a middleware that can apply those filters to the `IQueryable`. The execution engine will, in the end, execute the `IQueryable` and fetch the data.
+
+```csharp
+public class QueryType
+ : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.GetPersons(default))
+ .Type>>()
+ .UseFiltering();
+ }
+}
+
+public class Query
+{
+ public IQueryable GetPersons([Service]IPersonRepository repository)
+ {
+ repository.GetPersons();
+ }
+}
+```
+
+**Pure Code First**
+
+The field descriptor attribute `[UseFiltering]` does apply the extension method `UseFiltering()` on the field descriptor.
+
+```csharp
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetPersons([Service]IPersonRepository repository)
+ {
+ repository.GetPersons();
+ }
+}
+```
+
+**Schema First**
+
+> ⚠️ **Note:** Schema first does currently not support filtering!
+
+# Customizing Filters
+
+A `FilterInputType` defines a GraphQL input type, that Hot Chocolate uses for filtering. You can customize these similar to a normal input type. You can change the name of the type; add, remove, or change operations or directive; and configure the binding behavior. To define and customize a filter we must inherit from `FilterInputType` and configure it like any other type by overriding the `Configure` method.
+
+```csharp
+public class PersonFilterType
+ : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor
+ .BindFieldsExplicitly()
+ .Filter(t => t.Name)
+ .BindOperationsExplicitly()
+ .AllowEquals().Name("equals").And()
+ .AllowContains().Name("contains").And()
+ .AllowIn().Name("in");
+ }
+}
+```
+
+The above filter type defines explicitly which fields allow filtering and what operations these filters allow. Additionally, the filter type changes the name of the equals operation of the filter of the field `Name` to `equals`.
+
+To make use of the configuration in this filter type, you can provide it to the `UseFiltering` extension method as the generic type argument.
+
+```csharp
+public class QueryType
+ : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.GetPerson(default))
+ .Type>>();
+ .UseFiltering()
+ }
+}
+```
+
+# Sorting
+
+Like with filter support you can add sorting support to your database queries.
+
+```csharp
+public class QueryType
+ : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.GetPerson(default))
+ .Type>>();
+ .UseSorting()
+ }
+}
+```
+
+> ⚠️ **Note**: Be sure to install the `HotChocolate.Types.Sorting` NuGet package.
+
+If you want to combine for instance paging, filtering, and sorting make sure that the order is like follows:
+
+```csharp
+public class QueryType
+ : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.GetPerson(default))
+ .UsePaging()
+ .UseFiltering()
+ .UseSorting();
+ }
+}
+```
+
+**Why is order important?**
+
+Paging, filtering, and sorting are modular middlewares that form the field resolver pipeline.
+
+The above example forms the following pipeline:
+
+`Paging -> Filtering -> Sorting -> Field Resolver`
+
+The paging middleware will first delegate to the next middleware, which is filtering.
+
+The filtering middleware will also first delegate to the next middleware, which is sorting.
+
+The sorting middleware will again first delegate to the next middleware, which is the actual field resolver.
+
+The field resolver will call `GetPerson` which returns in this example an `IQueryable`. The queryable represents a not yet executed database query.
+
+After the resolver has been executed and puts its result onto the middleware context the sorting middleware will apply for the sort order on the query.
+
+After the sorting middleware has been executed and updated the result on the middleware context the filtering middleware will apply its filters on the queryable and updates the result on the middleware context.
+
+After the paging middleware has been executed and updated the result on the middleware context the paging middleware will slice the data and execute the queryable which will then actually pull in data from the data source.
+
+So, if we, for instance, applied paging as our last middleware the data set would have been sliced first and then filtered which in most cases is not what we actually want.
+
+# Filter & Operations Kinds
+
+You can break down filtering into different kinds of filters that then have different operations.
+The filter kind is bound to the type. A string is fundamentally something different than an array or an object.
+Each filter kind has different operations that you can apply to it. Some operations are unique to a filter and some operations are shared across multiple filter
+e.g. A string filter has string specific operations like `Contains` or `EndsWith` but still shares the operations `Equals` and `NotEquals` with the boolean filter.
+
+## Filter Kinds
+
+Hot Chocolate knows following filter kinds
+
+| Kind | Operations |
+| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| String | Equals, In, EndsWith, StartsWith, Contains, NotEquals, NotIn, NotEndsWith, NotStartsWith, NotContains |
+| Bool | Equals, NotEquals |
+| Object | Equals |
+| Array | Some, Any, All, None |
+| Comparable | Equals, In, GreaterThan, GreaterThanOrEqual, LowerThan, LowerThanOrEqual, NotEquals, NotIn, NotGreaterThan, NotGreaterThanOrEqual, NotLowerThan, NotLowerThanOrEqual |
+
+## Operations Kinds
+
+Hot Chocolate knows following operation kinds
+
+| Kind | Operations |
+| ---------------------- | ----------------------------------------------------------------------------------------------------- |
+| Equals | Compares the equality of input value and property value |
+| NotEquals | negation of Equals |
+| In | Checks if the property value is contained in a given list of input values |
+| NotIn | negation of In |
+| GreaterThan | checks if the input value is greater than the property value |
+| NotGreaterThan | negation of GreaterThan |
+| GreaterThanOrEquals | checks if the input value is greater than or equal to the property value |
+| NotGreaterThanOrEquals | negation of GreaterThanOrEquals |
+| LowerThan | checks if the input value is lower than the property value |
+| NotLowerThan | negation of LowerThan |
+| LowerThanOrEquals | checks if the input value is lower than or equal to the property value |
+| NotLowerThanOrEquals | negation of LowerThanOrEquals |
+| EndsWith | checks if the property value ends with the input value |
+| NotEndsWith | negation of EndsWith |
+| StartsWith | checks if the property value starts with the input value |
+| NotStartsWith | negation of StartsWith |
+| Contains | checks if the input value is contained in the property value |
+| NotContains | negation of Contains |
+| Some | checks if at least one element in the collection exists |
+| Some | checks if at least one element of the property value meets the condition provided by the input value |
+| None | checks if no element of the property value meets the condition provided by the input value |
+| All | checks if all least one element of the property value meets the condition provided by the input value |
+
+## Boolean Filter
+
+In this example, we look at the filter configuration of a Boolean filter.
+As an example, we will use the following model:
+
+```csharp
+public class User
+{
+ public bool IsOnline {get;set;}
+}
+
+public class Query : ObjectType
+{
+ [UseFiltering]
+ public IQueryable GetUsers([Service]UserService users )
+ => users.AsQueryable();
+}
+
+```
+
+The produced GraphQL SDL will look like the following:
+
+```graphql
+type Query {
+ users(where: UserFilter): [User]
+}
+
+type User {
+ isOnline: Boolean
+}
+
+input UserFilter {
+ isOnline: Boolean
+ isOnline_not: Boolean
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+### Boolean Operation Descriptor
+
+The example above showed that configuring the operations is optional.
+If you want to have access to the actual field input types or allow only a subset of Boolean filters for a given property, you can configure the operation over the `IFilterInputTypeDescriptor`
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Filter(x => x.Name)
+ .AllowEquals().And()
+ .AllowNotEquals();
+ }
+}
+```
+
+## Comparable Filter
+
+In this example, we look at the filter configuration of a comparable filter.
+
+A comparable filter is generated for all values that implement IComparable except string and boolean.
+e.g. `csharp±enum`, `csharp±int`, `csharp±DateTime`...
+
+As an example, we will use the following model:
+
+```csharp
+public class User
+{
+ public int LoggingCount {get;set;}
+}
+
+public class Query : ObjectType
+{
+ [UseFiltering]
+ public IQueryable GetUsers([Service]UserService users )
+ => users.AsQueryable();
+}
+
+```
+
+The produced GraphQL SDL will look like the following:
+
+```graphql
+type Query {
+ users(where: UserFilter): [User]
+}
+
+type User {
+ loggingCount: Int
+}
+
+input UserFilter {
+ loggingCount: Int
+ loggingCount_gt: Int
+ loggingCount_gte: Int
+ loggingCount_in: [Int!]
+ loggingCount_lt: Int
+ loggingCount_lte: Int
+ loggingCount_not: Int
+ loggingCount_not_gt: Int
+ loggingCount_not_gte: Int
+ loggingCount_not_in: [Int!]
+ loggingCount_not_lt: Int
+ loggingCount_not_lte: Int
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+### Comparable Operation Descriptor
+
+The example above showed that configuring the operations is optional.
+If you want to have access to the actual field input types or allow only a subset of comparable filters for a given property, you can configure the operation over the `IFilterInputTypeDescriptor`
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Filter(x => x.Name)
+ .AllowEquals().And()
+ .AllowNotEquals().And()
+ .AllowGreaterThan().And()
+ .AllowNotGreaterThan().And()
+ .AllowGreaterThanOrEqals().And()
+ .AllowNotGreaterThanOrEqals().And()
+ .AllowLowerThan().And()
+ .AllowNotLowerThan().And()
+ .AllowLowerThanOrEqals().And()
+ .AllowNotLowerThanOrEqals().And()
+ .AllowIn().And()
+ .AllowNotIn();
+ }
+}
+```
+
+## String Filter
+
+In this example, we look at the filter configuration of a String filter.
+As an example, we will use the following model:
+
+```csharp
+public class User
+{
+ public string Name {get;set;}
+}
+
+public class Query : ObjectType
+{
+ [UseFiltering]
+ public IQueryable GetUsers([Service]UserService users )
+ => users.AsQueryable();
+}
+
+```
+
+The produced GraphQL SDL will look like the following:
+
+```graphql
+type Query {
+ users(where: UserFilter): [User]
+}
+
+type User {
+ name: String
+}
+
+input UserFilter {
+ name: String
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ name_not: String
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+### String Operation Descriptor
+
+The example above showed that configuring the operations is optional.
+If you want to have access to the actual field input types or allow only a subset of string filters for a given property, you can configure the operation over the `IFilterInputTypeDescriptor`
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Filter(x => x.Name)
+ .AllowEquals().And()
+ .AllowNotEquals().And()
+ .AllowContains().And()
+ .AllowNotContains().And()
+ .AllowStartsWith().And()
+ .AllowNotStartsWith().And()
+ .AllowEndsWith().And()
+ .AllowNotEndsWith().And()
+ .AllowIn().And()
+ .AllowNotIn();
+ }
+}
+```
+
+## Object Filter
+
+In this example, we look at the filter configuration of an object filter.
+
+Hot Chocolate generated object filters for all objects. Since Version 11, Hot Chocolate also generates filter types for nested objects. You can also use object filters to filter over database relations.
+
+As an example, we will use the following model:
+
+```csharp
+public class User
+{
+ public Address Address {get;set;}
+}
+
+public class Address
+{
+ public string Street {get;set;}
+
+ public bool IsPrimary {get;set;}
+}
+
+public class Query : ObjectType
+{
+ [UseFiltering]
+ public IQueryable GetUsers([Service]UserService users )
+ => users.AsQueryable();
+}
+
+```
+
+The produced GraphQL SDL will look like the following:
+
+```graphql
+type Query {
+ users(where: UserFilter): [User]
+}
+
+type User {
+ address: Address
+}
+
+type Address {
+ isPrimary: Boolean
+ street: String
+}
+
+input UserFilter {
+ address: AddressFilter
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+
+input AddressFilter {
+ is_primary: Boolean
+ is_primary_not: Boolean
+ street: String
+ street_contains: String
+ street_ends_with: String
+ street_in: [String]
+ street_not: String
+ street_not_contains: String
+ street_not_ends_with: String
+ street_not_in: [String]
+ street_not_starts_with: String
+ street_starts_with: String
+ AND: [AddressFilter!]
+ OR: [AddressFilter!]
+}
+```
+
+### Object Operation Descriptor
+
+The example above showed that configuring the operations is optional.
+If you want to have access to the actual field input types or allow only a subset of comparable filters for a given property, you can configure the operation over the `IFilterInputTypeDescriptor`
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Object(x => x.Address);
+ }
+}
+```
+
+**Configuring a custom nested filter type:**
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Object(x => x.Address).AllowObject();
+ }
+}
+
+public class AddressFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Filter(x => x.IsPrimary);
+ }
+}
+
+// or inline
+
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Object(x => x.Address)
+ .AllowObject(
+ y => y.BindFieldsExplicitly().Filter(z => z.IsPrimary));
+ }
+}
+
+
+```
+
+## List Filter
+
+In this example, we look at the filter configuration of a list filter.
+
+Hot Chocolate can also generate filters for IEnumerables. Like object filter, Hot Chocolate generates filters for the whole object tree. List filter addresses scalars and object values differently.
+In the case the field is a scalar value, Hot Chocolate creates and object type to address the different operations of this scalar. e.g. If you specify filters for a list of strings, Hot Chocolate creates an object type that contains all operations of the string filter.
+In case the list holds a complex object, it generates an object filter for this object instead.
+
+Hot Chocolate implicitly generates filters for all properties that implement `IEnumerable`.
+e.g. `csharp±string[]`, `csharp±List`, `csharp±IEnumerable`...
+
+As an example, we will use the following model:
+
+```csharp
+public class User
+{
+ public string[] Roles {get;set;}
+
+ public IEnumerable Addresses {get;set;}
+}
+
+public class Address
+{
+ public string Street {get;set;}
+
+ public bool IsPrimary {get;set;}
+}
+
+public class Query : ObjectType
+{
+ [UseFiltering]
+ public IQueryable GetUsers([Service]UserService users )
+ => users.AsQueryable();
+}
+
+```
+
+The produced GraphQL SDL will look like the following:
+
+```graphql
+type Query {
+ users(where: UserFilter): [User]
+}
+
+type User {
+ addresses: [Address]
+ roles: [String]
+}
+
+type Address {
+ isPrimary: Boolean
+ street: String
+}
+
+input UserFilter {
+ addresses_some: AddressFilter
+ addresses_all: AddressFilter
+ addresses_none: AddressFilter
+ addresses_any: Boolean
+ roles_some: ISingleFilterOfStringFilter
+ roles_all: ISingleFilterOfStringFilter
+ roles_none: ISingleFilterOfStringFilter
+ roles_any: Boolean
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+
+input AddressFilter {
+ is_primary: Boolean
+ is_primary_not: Boolean
+ street: String
+ street_contains: String
+ street_ends_with: String
+ street_in: [String]
+ street_not: String
+ street_not_contains: String
+ street_not_ends_with: String
+ street_not_in: [String]
+ street_not_starts_with: String
+ street_starts_with: String
+ AND: [AddressFilter!]
+ OR: [AddressFilter!]
+}
+
+input ISingleFilterOfStringFilter {
+ AND: [ISingleFilterOfStringFilter!]
+ element: String
+ element_contains: String
+ element_ends_with: String
+ element_in: [String]
+ element_not: String
+ element_not_contains: String46
+ element_not_ends_with: String
+ element_not_in: [String]
+ element_not_starts_with: String
+ element_starts_with: String
+ OR: [ISingleFilterOfStringFilter!]
+}
+```
+
+### Array Operation Descriptor
+
+The example above showed that configuring the operations is optional.
+If you want to have access to the actual field input types or allow only a subset of array filters for a given property, you can configure the operation over the `IFilterInputTypeDescriptor`
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.List(x => x.Addresses)
+ .AllowSome().And()
+ .AlloAny().And()
+ .AllowAll().And()
+ .AllowNone();
+ descriptor.List(x => x.Roles)
+ .AllowSome().And()
+ .AlloAny().And()
+ .AllowAll().And()
+ .AllowNone();
+ }
+}
+```
+
+# Naming Conventions
+
+\_Hot Chocolate already provides two naming schemes for filters. If you would like to define your own naming scheme or extend existing ones have a look at the documentation of TODO:Link-Filtering
+
+## Snake Case
+
+**Configuration**
+You can configure the Snake Case with the `UseSnakeCase` extension method convention on the `IFilterConventionDescriptor`
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor.UseSnakeCase()
+ }
+}
+
+SchemaBuilder.New().AddConvention();
+//
+SchemaBuilder.New().AddConvention(new FilterConvention(x => x.UseSnakeCase())
+```
+
+```graphql
+input FooBarFilter {
+ AND: [FooBarFilter!]
+ nested: String
+ nested_contains: String
+ nested_ends_with: String
+ nested_in: [String]
+ nested_not: String
+ nested_not_contains: String
+ nested_not_ends_with: String
+**Change the name of an operation**
+ nested_not_in: [String]
+ nested_not_starts_with: String
+ nested_starts_with: String
+ OR: [FooBarFilter!]
+}
+
+input FooFilter {
+ AND: [FooFilter!]
+ bool: Boolean
+ bool_not: Boolean
+ comparable: Short
+ comparableEnumerable_all: ISingleFilterOfInt16Filter
+ comparableEnumerable_any: Boolean
+ comparableEnumerable_none: ISingleFilterOfInt16Filter
+ comparableEnumerable_some: ISingleFilterOfInt16Filter
+ comparable_gt: Short
+ comparable_gte: Short
+ comparable_in: [Short!]
+ comparable_lt: Short
+ comparable_lte: Short
+ comparable_not: Short
+ comparable_not_gt: Short
+ comparable_not_gte: Short
+ comparable_not_in: [Short!]
+ comparable_not_lt: Short
+ comparable_not_lte: Short
+ object: FooBarFilter
+ OR: [FooFilter!]
+}
+
+input ISingleFilterOfInt16Filter {
+ AND: [ISingleFilterOfInt16Filter!]
+ element: Short
+ element_gt: Short
+ element_gte: Short
+ element_in: [Short!]
+ element_lt: Short
+ element_lte: Short
+ element_not: Short
+ element_not_gt: Short
+ element_not_gte: Short
+ element_not_in: [Short!]
+ element_not_lt: Short
+ element_not_lte: Short
+ OR: [ISingleFilterOfInt16Filter!]
+}
+```
+
+## Pascal Case
+
+**Configuration**
+You can configure the Pascal Case with the `UsePascalCase` extension method convention on the `IFilterConventionDescriptor`
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor.UsePascalCase()
+ }
+}
+
+SchemaBuilder.New().AddConvention();
+//
+SchemaBuilder.New().AddConvention(new FilterConvention(x => x.UsePascalCase())
+```
+
+```graphql
+input FooBarFilter {
+ AND: [FooBarFilter!]
+ Nested: String
+ Nested_Contains: String
+ Nested_EndsWith: String
+ Nested_In: [String]
+ Nested_Not: String
+ Nested_Not_Contains: String
+ Nested_Not_EndsWith: String
+ Nested_Not_In: [String]
+ Nested_Not_StartsWith: String
+ Nested_StartsWith: String
+ OR: [FooBarFilter!]
+}
+
+input FooFilter {
+ AND: [FooFilter!]
+ Bool: Boolean
+ Bool_Not: Boolean
+ Comparable: Short
+ ComparableEnumerable_All: ISingleFilterOfInt16Filter
+ ComparableEnumerable_Any: Boolean
+ ComparableEnumerable_None: ISingleFilterOfInt16Filter
+ ComparableEnumerable_Some: ISingleFilterOfInt16Filter
+ Comparable_Gt: Short
+ Comparable_Gte: Short
+ Comparable_In: [Short!]
+ Comparable_Lt: Short
+ Comparable_Lte: Short
+ Comparable_Not: Short
+ Comparable_Not_Gt: Short
+ Comparable_Not_Gte: Short
+ Comparable_Not_In: [Short!]
+ Comparable_Not_Lt: Short
+ Comparable_Not_Lte: Short
+ Object: FooBarFilter
+ OR: [FooFilter!]
+}
+
+input ISingleFilterOfInt16Filter {
+ AND: [ISingleFilterOfInt16Filter!]
+ Element: Short
+ Element_Gt: Short
+ Element_Gte: Short
+ Element_In: [Short!]
+ Element_Lt: Short
+ Element_Lte: Short
+ Element_Not_Gt: Short
+ Element_Not: Short
+ Element_Not_Gte: Short
+ Element_Not_In: [Short!]
+ Element_Not_Lt: Short
+ Element_Not_Lte: Short
+ OR: [ISingleFilterOfInt16Filter!]
+}
+```
+
+# Customizing Filter
+
+Hot Chocolate provides different APIs to customize filtering. You can write custom filter input types, customize the inference behavior of .NET Objects, customize the generated expression, or create a custom visitor, and attach your exotic database.
+
+**As this can be a bit overwhelming the following questionnaire might help:**
+
+| | |
+| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
+| _You do not want all the generated filters and only allow a specific set of filters in a specific case?_ | Custom FilterInputType |
+| _You want to change the name of a field or a whole type?_ | Custom FilterInputType |
+| _You want to change the name of the `where` argument?_ | Filter Conventions ArgumentName |
+| _You want to configure how *Hot Chocolate* generates the name and the description of filters in globally? e.g. `PascalCaseFilterType`?_ | Filter Conventions |
+| _You want to configure what the different types of filters are allowed globally?_ | Filter Conventions |
+| _Your database provider does not support certain operations of `IQueryable`_ | Filter Conventions |
+| _You want to change the naming of a specific lar filter type? e.g._ `foo_contains` _should be_ `foo_like` | Filter Conventions |
+| _You want to customize the expression a filter is generating: e.g._ `_equals` _should not be case sensitive?_ | Expression Visitor |
+| _You want to create your own filter types with custom parameters and custom expressions? e.g. GeoJson?_ | Filter Conventions |
+| _You have a database client that does not support `IQueryable` and wants to generate filters for it?_ | Custom Visitor |
+
+# Custom FilterInputType
+
+Under the hood, filtering is based on top of normal Hot Chocolate input types. You can easily customize them with a very familiar fluent interface. The filter input types follow the same `descriptor` scheme as you are used to from the normal filter input types. Just extend the base class `FilterInputType` and override the descriptor method.
+
+```csharp
+public class User
+{
+ public string Name {get; set; }
+
+ public string LastName {get; set; }
+}
+
+public class UserFilterType
+ : FilterInputType
+{
+ protected override void Configure( IFilterInputTypeDescriptor descriptor) {
+
+ }
+}
+```
+
+`IFilterInputTypeDescriptor` supports most of the methods of `IInputTypeDescriptor` and adds the configuration interface for the filters. By default, Hot Chocolate generates filters for all properties of the type.
+If you do want to specify the filters by yourself you can change this behavior with `BindFields`, `BindFieldsExplicitly` or `BindFieldsImplicitly`.
+
+```csharp
+public class UserFilterType
+ : FilterInputType
+{
+ protected override void Configure( IFilterInputTypeDescriptor descriptor) {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Filter(x => x.Name);
+ }
+}
+```
+
+```graphql
+input UserFilter {
+ name: String
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ name_not: String
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+To add or customize a filter you must use `Filter(x => x.Foo)` for scalars `List(x => x.Bar)` for lists and `Object(x => x.Baz)` for nested objects.
+These methods will return fluent interfaces to configure the filter for the selected field.
+
+A field has different filter operations that you can configure. You will find more about filter types and filter operations here TODO:Link
+When fields are bound implicitly, meaning filters are added for all properties, you may want to hide a few fields. You can do this with `Ignore(x => Bar)`.
+Operations on fields can again be bound implicitly or explicitly. By default, Hot Chocolate generates operations for all fields of the type.
+If you do want to specify the operations by yourself you can change this behavior with `BindFilters`, `BindFiltersExplicitly` or `BindFiltersImplicitly`.
+
+It is also possible to customize the GraphQL field of the operation further. You can change the name, add a description or directive.
+
+```csharp
+public class UserFilterType
+ : FilterInputType
+{
+ protected override void Configure( IFilterInputTypeDescriptor descriptor) {
+ // descriptor.BindFieldsImplicitly(); <- is already the default
+ descriptor.Filter(x => x.Name)
+ .BindFilterExplicitly()
+ .AllowContains()
+ .Description("Checks if the provided string is contained in the `Name` of a User")
+ .And()
+ .AllowEquals()
+ .Name("exits_with_name")
+ .Directive("name");
+ descriptor.Ignore(x => x.Bar);
+ }
+}
+```
+
+```graphql
+input UserFilter {
+ exits_with_name: String @name
+ """
+ Checks if the provided string is contained in the `Name` of a User
+ """
+ name_contains: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+**API Documentation**
+
+| Method | Description |
+| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
+| `csharp±BindFields(BindingBehavior bindingBehavior)` | Defines the filter binding behavior. `Explicitly`or `Implicitly`. Default is `Implicitly` |
+| `csharp±BindFieldsExplicitly` | Defines that all filters have to be specified explicitly. This means that only the filters are applied that are added with `Filter(x => x.Foo)` |
+| `csharp±BindFieldsImplicitly` | The filter type will add filters for all compatible fields. |
+| `csharp±Description(string value)` | Adds explanatory text of the `FilterInputType` that can be accessed via introspection. |
+| `csharp±Name(NameString value)` | Defines the _GraphQL_ name of the `FilterInputType`. |
+| `csharp±Ignore( Expression> property);` | Ignore the specified property. |
+| `csharp±Filter( Expression> property)` | Defines a string filter for the selected property. |
+| `csharp±Filter( Expression> property)` | Defines a bool filter for the selected property. |
+| `csharp±Filter( Expression> property)` | Defines a comparable filter for the selected property. |
+| `csharp±Object( Expression> property)` | Defines a object filter for the selected property. |
+| `csharp±List( Expression>> property)` | Defines an array string filter for the selected property. |
+| `csharp±List( Expression>> property)` | Defines an array bool filter for the selected property. |
+| `csharp±List( Expression>> property)` | Defines an array comarable filter for the selected property. |
+| `csharp±Filter( Expression>> property)` | Defines an array object filter for the selected property. |
+| `csharp±Directive(TDirective directiveInstance)` | Add directive `directiveInstance` to the type |
+| `csharp±Directive(TDirective directiveInstance)` | Add directive of type `TDirective` to the type |
+| `csharp±Directive(NameString name, params ArgumentNode[] arguments)` | Add directive of type `TDirective` to the type |
+
+# Filter Conventions
+
+The customization of filters with `FilterInputTypes` works if you only want to customize one specific filter type.
+If you want to change the behavior of all filter types, you want to create a convention for your filters. The filter convention comes with a fluent interface that is close to a type descriptor.
+You can see the convention as a configuration object that holds the state that is used by the type system or the execution engine.
+
+## Get Started
+
+To use a filter convention, you can extend `FilterConvention` and override the `Configure` method. Alternatively, you can directly configure the convention over the constructor argument.
+You then must register your custom convention on the schema builder with `AddConvention`.
+
+```csharp
+public class CustomConvention
+ : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor) { }
+}
+
+SchemaBuilder.New().AddConvention();
+//
+SchemaBuilder.New().AddConvention(new FilterConvention(x => /* Config */));
+```
+
+## Convention Descriptor Basics
+
+In this section, we will take a look at the basic features of the filter convention.
+The documentation will reference often to `descriptor`. Imagine this `descriptor` as the parameter of the Configure method of the filter convention in the following context:
+
+```csharp {5}
+public class CustomConvention
+ : FilterConvention
+{
+ protected override void Configure(
+ IFilterConventionDescriptor descriptor
+ ) { }
+}
+
+SchemaBuilder.New().AddConvention();
+```
+
+
+
+### Argument Name
+
+With the convention descriptor, you can easily change the argument name of the `FilterInputType`.
+
+**Configuration**
+
+```csharp
+descriptor.ArgumentName("example_argument_name");
+```
+
+**Result**
+
+```graphql
+type Query {
+ users(example_argument_name: UserFilter): [User]
+}
+```
+
+### Change Name of Scalar List Type Element
+
+You can change the name of the element of the list type.
+
+**Configuration**
+
+```csharp
+descriptor.ElementName("example_element_name");
+```
+
+**Result**
+
+```graphql
+input ISingleFilterOfInt16Filter {
+ AND: [ISingleFilterOfInt16Filter!]
+ example_element_name: Short
+ example_element_name_gt: Short
+ example_element_name_gte: Short
+ example_element_name_in: [Short!]
+ example_element_name_lt: Short
+ example_element_name_lte: Short
+ example_element_name_not: Short
+ example_element_name_not_gt: Short
+ example_element_name_not_gte: Short
+ example_element_name_not_in: [Short!]
+ example_element_name_not_lt: Short
+ example_element_name_not_lte: Short
+ OR: [ISingleFilterOfInt16Filter!]
+}
+```
+
+### Configure Filter Type Name Globally
+
+You can change the way Hot Chocolate names the types by supplying a delegate.
+
+This delgate must be of the following type:
+
+```csharp
+public delegate NameString GetFilterTypeName(
+ IDescriptorContext context,
+ Type entityType);
+```
+
+**Configuration**
+
+```csharp
+descriptor.TypeName((context,types) =>
+ context.Naming.GetTypeName(entityType, TypeKind.Object) + "Custom");
+```
+
+**Result**
+
+```graphql
+type Query {
+ users(where: UserCustom): [User]
+}
+```
+
+### Configure Filter Description Globally
+
+To change the way filter types are named, you have to exchange the factory.
+
+You have to provide a delegate of the following type:
+
+```csharp
+public delegate string GetFilterTypeDescription(
+ IDescriptorContext context,
+ Type entityType);
+```
+
+**Configuration**
+
+```csharp
+descriptor.TypeName((context,types) =>
+ context.Naming.GetTypeDescription(entityType, TypeKind.Object); + "Custom");
+```
+
+**Result**
+
+```graphql
+"""
+Custom
+"""
+input UserFilter {
+ AND: [UserFilter!]
+ isOnline: Boolean
+ isOnline_not: Boolean
+ OR: [UserFilter!]
+}
+```
+
+### Reset Configuration
+
+Hot Chocolate shippes with well-defined defaults. To start from scratch, you need to call `Reset()`first.
+
+**Configuration**
+
+```csharp
+descriptor.Reset();
+```
+
+**Result**
+
+> **⚠ Note:** You will need to add a complete configuration, otherwise the filter will not work as desired!
+
+## Describe with convention
+
+With the filter convention descriptor, you have full control over what filters are inferred, their names, operations, and a lot more.
+The convention provides a familiar interface to the type configuration. We recommended to first take a look at `Filter & Operations` to understand the concept of filters. This will help you understand how the filter configuration works.
+
+Filtering has two core components at its heart. First, you have the inference of filters based on .NET types. The second part is an interceptor that translates the filters to the desired output and applies it to the resolver pipeline. These two parts can (and have to) be configured completely independently. With this separation, it is possible to easily extend the behavior. The descriptor is designed to be extendable by extension methods.
+
+**It's fluent**
+
+Filter conventions are a completely fluent experience. You can write a whole configuration as a chain of method calls.
+This provides a very clean interface, but can, on the other hand, get messy quickly. We recommend using indentation to keep the configuration comprehensible.
+You can drill up with `And()`.
+
+```csharp
+ descriptor.Operation(FilterOperationKind.Equals).Description("has to be equal");
+ descriptor.Operation(FilterOperationKind.NotEquals).Description("has not to be equal");
+ descriptor.Type(FilterKind.Comparable).Operation(FilterOperationKind.NotEquals).Description("has to be comparable and not equal")
+
+
+ descriptor
+ .Operation(FilterOperationKind.Equals)
+ .Description("has to be equal")
+ .And()
+ .Operation(FilterOperationKind.NotEquals)
+ .Description("has not to be equal")
+ .And()
+ .Type(FilterKind.Comparable)
+ .Operation(FilterOperationKind.NotEquals)
+ .Description("has to be comparable and not equal")
+```
+
+### Configuration of the type system
+
+In this section, we will focus on the generation of the schema. If you are interested in changing how filters translate to the database, you have to look here TODO:Link
+
+#### Configure Filter Operations
+
+There are two ways to configure Operations.
+
+You can configure a default configuration that applies to all operations of this kind. In this case the configuration for `FilterOperationKind.Equals` would be applied to all `FilterKind` that specify this operation.
+
+```csharp
+ descriptor.Operation(FilterOperationKind.Equals)
+```
+
+If you want to configure a more specific Operation e.g. `FilterOperationKind.Equal` of kind `FilterKind.String`, you can override the default behavior.
+
+```csharp
+ descriptor.Type(FilterKind.String).Operation(FilterOperationKind.Equals)
+```
+
+The operation descriptor allows you to configure the name, the description or even ignore an operation completely
+
+In this example, we will look at the following input type:
+
+```graphql
+input UserFilter {
+ loggingCount: Int
+ loggingCount_gt: Int
+ loggingCount_gte: Int
+ loggingCount_in: [Int!]
+ loggingCount_lt: Int
+ loggingCount_lte: Int
+ loggingCount_not: Int
+ loggingCount_not_gt: Int
+ loggingCount_not_gte: Int
+ loggingCount_not_in: [Int!]
+ loggingCount_not_lt: Int
+ loggingCount_not_lte: Int
+ name: String
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ name_not: String
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+##### Change the name of an operation
+
+To change the name of an operation you need to specify a delegate of the following type:
+
+```csharp
+public delegate NameString CreateFieldName(
+ FilterFieldDefintion definition,
+ FilterOperationKind kind);
+```
+
+**Configuration**
+
+```csharp {1, 6}
+ // (A)
+ // specifies that all not equals operations should be extended with _nada
+ descriptor
+ .Operation(FilterOperationKind.NotEquals)
+ .Name((def, kind) => def.Name + "_nada" );
+ // (B)
+ // specifies that the not equals operations should be extended with _niente.
+ // this overrides (A)
+ descriptor
+ .Type(FilterKind.Comparable)
+ .Operation(FilterOperationKind.NotEquals)
+ .Name((def, kind) => def.Name + "_niente" )
+```
+
+**result**
+
+```graphql {8,18}
+input UserFilter {
+ loggingCount: Int
+ loggingCount_gt: Int
+ loggingCount_gte: Int
+ loggingCount_in: [Int!]
+ loggingCount_lt: Int
+ loggingCount_lte: Int
+ loggingCount_niente: Int <-- (B)
+ loggingCount_not_gt: Int
+ loggingCount_not_gte: Int
+ loggingCount_not_in: [Int!]
+ loggingCount_not_lt: Int
+ loggingCount_not_lte: Int
+ name: String
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ name_nada: String <-- (A)
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+##### Change the description of an operation
+
+In the same way, you can configure names you can also configure the description of operations.
+You can either set the description for all operations of this kind or only for a specific one in combination with a filter kind.
+
+**Configuration**
+
+```csharp
+ descriptor
+ .Operation(FilterOperationKind.Equals)
+ .Description("has to be equal")
+ .And()
+ .Operation(FilterOperationKind.NotEquals)
+ .Description("has not to be equal")
+ .And()
+ .Type(FilterKind.Comparable)
+ .Operation(FilterOperationKind.NotEquals)
+ .Description("has to be comparable and not equal")
+```
+
+**result**
+
+```graphql {2-4,11-14, 20-22,27-29}
+input UserFilter {
+ """
+ has to be equal
+ """
+ loggingCount: Int
+ loggingCount_gt: Int
+ loggingCount_gte: Int
+ loggingCount_in: [Int!]
+ loggingCount_lt: Int
+ loggingCount_lte: Int
+ """
+ has to be comparable and not equal
+ """
+ loggingCount_not: Int
+ loggingCount_not_gt: Int
+ loggingCount_not_gte: Int
+ loggingCount_not_in: [Int!]
+ loggingCount_not_lt: Int
+ loggingCount_not_lte: Int
+ """
+ has to be equal
+ """
+ name: String
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ """
+ has not to be equal
+ """
+ name_not: String
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+##### Hide Operations
+
+Hot Chocolate comes preconfigured with a set of operations. If you like to hide operations globally, you can use `Ignore` for it.
+If your database provider does not support certain `IQueryable` methods you can just ignore the operation. Ignored operations do not generate filter input types.
+
+There are multiple ways to ignore an operation:
+
+**Configuration**
+
+```csharp
+ descriptor
+ .Ignore(FilterOperationKind.Equals)
+ .Operation(FilterOperationKind.NotEquals)
+ .Ignore()
+ .And()
+ .Type(FilterKind.Comparable)
+ .Operation(FilterOperationKind.GreaterThanOrEqual)
+ .Ignore();
+```
+
+**result**
+
+```graphql {2,4, 8,14,18}
+input UserFilter {
+ ↵
+ loggingCount_gt: Int
+ ↵
+ loggingCount_in: [Int!]
+ loggingCount_lt: Int
+ loggingCount_lte: Int
+ ↵
+ loggingCount_not_gt: Int
+ loggingCount_not_gte: Int
+ loggingCount_not_in: [Int!]
+ loggingCount_not_lt: Int
+ loggingCount_not_lte: Int
+ ↵
+ name_contains: String
+ name_ends_with: String
+ name_in: [String]
+ ↵
+ name_not_contains: String
+ name_not_ends_with: String
+ name_not_in: [String]
+ name_not_starts_with: String
+ name_starts_with: String
+ AND: [UserFilter!]
+ OR: [UserFilter!]
+}
+```
+
+##### Configure Implicit Filter
+
+The default binding behavior of Hot Chocolate is implicit. Filter types are no exception.
+This first may seem like magic, but unfortunately, there is none. It is just code. With `AddImplicitFilter` you can add this pinch of magic to your extension too.
+Hot Chocolate creates the filters as it builds the input type. The type iterates over a list of factories sequentially and tries to create a definition for each property. The first factory that can handle the property wins and creates a definition for the filter.
+
+To configure you have to use the following delegate:
+
+```csharp
+ public delegate bool TryCreateImplicitFilter(
+ IDescriptorContext context,
+ Type type,
+ PropertyInfo property,
+ IFilterConvention filterConventions,
+ [NotNullWhen(true)] out FilterFieldDefintion? definition);
+```
+
+| parameter | type | description |
+| ------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- |
+| _context_ | `IDescriptorContext` | The context of the type descriptor |
+| _type_ | `Type` | The type of the property. `Nullable` is already unwrapped (typeof(T)) |
+| _property_ | `PropertyInfo` | The property |
+| _filterConventions_ | `IFilterConvention` | The instance of the `IFilterContention`. |
+| _definition_ | `out FilterFieldDefintion?` | The generated definition for the property. Return null if the current factory cannot handle the property. |
+
+If you just want to build your extension for implicit bindings, you can just out a custom `FilterFieldDefinition`.
+
+It makes sense to encapsulate that logic in a FilterFieldDescriptor though. You can reuse this descriptor also for the fluent configuration interface.
+
+**Example**
+
+```csharp
+private static bool TryCreateStringFilter(
+ IDescriptorContext context,
+ Type type,
+ PropertyInfo property,
+ IFilterConvention filterConventions,
+ [NotNullWhen(true)] out FilterFieldDefintion? definition)
+{
+ if (type == typeof(string))
+ {
+ var field = new StringFilterFieldDescriptor(context, property, filterConventions);
+ definition = field.CreateDefinition();
+ return true;
+ }
+
+ definition = null;
+ return false;
+}
+```
+
+##### Creating a fluent filter extension
+
+Hot Chocolate provides fluent interfaces for all its APIs. If you want to create an extension that integrates seamlessly with Hot Chocolate it makes sense to also provide fluent interfaces. It makes sense to briefly understand how `Type -> Descriptor -> Definition` work. You can read more about it here //TODO LINK
+
+Here a quick introduction:
+
+_Type_
+
+A type is a description of a GraphQL Type System Object. Hot Chocolate builds types during schema creation. Types specify how a GraphQL Type looks like. It holds, for example, the definition, fields, interfaces, and all life cycle methods. Type do only exist on startup; they do not exist on runtime.
+
+_Type Definition_
+
+Each type has a definition that describes the type. It holds, for example, the name, description, the CLR type and the field definitions. The field definitions describe the fields that are on the type.
+
+_Type Descriptor_
+
+A type descriptor is a fluent interface to describe the type over the definition. The type descriptor does not have access to the type itself. It operates solely on the definition.
+
+In the case of filtering, this works nearly the same. The `FilterInputType` is just an extension of the `InputObjectType`. It also has the same _Definition_. The `FilterInputType` stores `FilterOperationField` on this definition. These are extensions of the normal `InputField`'s and extend it by a `FilterOperationKind`.
+
+With a normal `InputTypeDescriptor` you declare a field by selecting a member. The filter descriptor works a little differently. You declare the `FilterKind` of a member by selecting it and then you declare the operations on this filter. These operations are the input field configuration.
+
+```csharp
+InputTypeDescriptor inputDesc;
+inputDesc.Field(x => x.Name)
+ .Description("This is the name")
+
+
+FilterInputTypeDescriptor inputDesc;
+inputDesc.Filter(x => x.Name).AllowEqual().Description("This is the name")
+```
+
+We have a few case studies that will show you how you can change the inference:
+
+1. String "\_like" shows an example of how you can easily add a "\_like" operation to the string filter
+2. DateTime "from", "to"
+3. NetTopologySuite
+
+> The configuration you see in this case study only shows how you add an operation to an already-existing filter. After this, the job is only half way done. To create a working filter, you must also change the expression visitor. Check the documentation for //TODO: ExpressionVisitor
+
+##### Case Study: String "\_like"
+
+**Situation**
+The customer has requested a full-text search of the description field of a product. The product owner has promised the feature to the customer two sprints ago and it has still not been shipped. The UX guru of your company has, slightly under pressure, worked out a solution, and together with the frontend team they have already build a prototype. In the heat of the moment, they did not read the user story correctly and, unfortunately, realized last minute that the current filtering API does not fit their needs. The customer does also has to be able to create complex search queries. `This%Test` should match `This is a Test`. As you come back from lunch a hysterical product owner explains the situation to you. To you, it is immediately clear that this can be easily done by using the SQL `like` operator.
+
+In your codebase you use the `UseFiltering` middleware extensively. In some cases, you also have customized filter types. To cover all possible cases you need
+
+1. Implicit Binding: `[UseFiltering]` should automagically create the "\_like" filter for every string filter
+2. Explicity Binding: `desc.Filter(x => x.Description).AllowLike())`
+3. Expression Visitor: You want to directly filter on the database. You use EF Core.
+
+**Implicit Binding**
+With the conventions, it is easy to add operations on already existing filters. We will first look into the configuration for filter inference and in a second step into the code first extension.
+
+You just need to navigate to the filter you like to modify. `descriptor.Type(FilterKind.String)`. Just add the operation you need with `.Operation(FilterOperationKind.Like)`. The next step is to add factories for the name and the description.
+
+Altogether this looks like this:
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor
+ .Type(FilterKind.String)
+ .Operation(FilterOperationKind.GreaterThanOrEqual)
+ .Name((def, kind) => def.Name + "_like" );
+ .Description("Full text search. Use % as a placeholder for any symbol");
+ }
+}
+```
+
+**Explicit Binding**
+By extending the filter descriptor of the string filter you can add a fluent extension that seamlessly integrated with the Hot Chocolate API.
+
+//TODO: currently there `StringFilterOperationDescriptor` requires `StringFilterFieldDescriptor` instead of `StringFilterFieldDescriptor` and there is no way to `Allow`
+//TODO: TYPO ! FilterFieldDefintion
+//TODO: Move RewriteType to convention .
+//TODO: Move up CreateFieldName
+
+```csharp
+public static class StringLikeFilterExtension
+{
+ public static IStringFilterOperationDescriptor AllowLike(
+ IStringFilterFieldDescriptor descriptor)
+ {
+ return descriptor.Allow(
+ FilterOperationKind.ArrayAll,
+ (ctx, definition) =>
+ {
+ var operation = new FilterOperation(
+ typeof(string), FilterOperationKind.ArrayAll, definition.Property);
+
+ return StringFilterOperationDescriptor.New(
+ ctx,
+ descriptor,
+ ctx.GetFilterConvention().CreateFieldName(FilterOperationKind.ArrayAll),
+ ctx.GetFilterConvention().RewriteType(FilterOperationKind.ArrayAll),
+ operation);
+ }
+ )
+ }
+}
+```
+
+---
+
+##### Case Study: DateTime "from", "to"
+
+**Situation**
+
+1. Implicit Binding: `[UseFiltering]` should automagically create `DateTimeFilter` and the corresponding "\_from" and "\_to".
+2. Explicity Binding: `desc.Filter(x => x.OrderedAt).AllowFrom().AllowTo())`
+3. Expression Visitor: You want to directly filter on the database. You use EF Core.
+
+**Configuration**
+
+It is slightly more complex to create a custom filter than just modifying existing operations. There are a few different parts that must come together to make this work. Implicit and Explicit Bindings are coming together in this example.
+
+Let's start with the configuration of the convention. By splitting the configuration up into a set of extension methods that can be applied to the convention, it is possible to easily replace sub-components of the extension. e.g. some users might want to use an expression visitor, some others might want to use MognoDB Native.
+
+- `UseDateTimeFilter` adds support for date-time filters and registers the expression visitor for it. Abstraction for `UseDateTimeFilterImplicitly().UseDateTimeExpression()`
+
+- `UseDateTimeFilterImplicitly` only registers the configuration of the schema building part of the extension
+
+- `UseDateTimeExpression` only registers the expression visitor configuration.
+
+With this separation, a user that prefers to use a custom visitor, can just register the types and skip the expression visitor configuration
+
+TODO: UseExpressionVisitor should return expression visitor if it already exists
+TODO: Reference Definition from Filter Operation instead of property. This way we could reduce complexity further and improve extensibility
+
+```csharp
+public static class DateTimeFilterConventionExtensions
+{
+ public static IFilterConventionDescriptor UseDateTimeFilter(
+ this IFilterConventionDescriptor descriptor) =>
+ descriptor.UseDateTimeFilterImplicitly()
+ .UseDateTimeFilterExpression();
+
+ public static IFilterConventionDescriptor UseDateTimeFilterImplicitly(
+ this IFilterConventionDescriptor descriptor) =>
+ descriptor.AddImplicitFilter(TryCreateDateTimeFilter)
+ .Type(FilterKind.DateTime)
+ .Operation(FilterOperationKind.GreaterThanOrEquals)
+ .Name((def, _) => def.Name + "_from")
+ .Description("")
+ .And()
+ .Operation(FilterOperationKind.LowerThanOrEquals)
+ .Name((def, _) => def.Name + "_to")
+ .Description("")
+ .And()
+ .And();
+
+ public static IFilterConventionDescriptor UseDateTimeFilterExpression(
+ this IFilterConventionDescriptor descriptor) =>
+ descriptor.UseExpressionVisitor()
+ .Kind(FilterKind.DateTime)
+ .Operation(FilterOperationKind.LowerThanOrEquals)
+ .Handler(ComparableOperationHandlers.LowerThanOrEquals).And()
+ .Operation(FilterOperationKind.GreaterThanOrEquals)
+ .Handler(ComparableOperationHandlers.GreaterThanOrEquals).And()
+ .And()
+ .And();
+}
+```
+
+**Create Date Time Filter Implicitly**
+
+`DateTime` is a new filter. Hot Chocolate is only aware of its existence because of the delegate passed to `AddImplicitFilter`
+
+```csharp
+private static bool TryCreateDateTimeFilter(
+ IDescriptorContext context,
+ Type type,
+ PropertyInfo property,
+ IFilterConvention filterConventions,
+ [NotNullWhen(true)] out FilterFieldDefintion? definition)
+{
+ if (type == typeof(DateTime))
+ {
+ var field = new DateTimeFilterFieldDescriptor(
+ context, property, filterConventions);
+ definition = field.CreateDefinition();
+ return true;
+ }
+
+ definition = null;
+ return false;
+}
+```
+
+TODO: make filters name based
+**Filter Field**
+
+A filter field is a collection of operations. It holds the configuration of the different operations like _“from”_ and _“to”_. In classic Hot Chocolate fashion there is a descriptor that describes these collections. Hot Chocolate provides the base class `FilterFieldDescriptorBase` you can use as an extension point. There is quite a lot of boilerplate code you need to write. e.g. it makes sense to define an interface for the descriptor.
+You find an example here: //TODO LINK
+
+For the explicit binding, we need to override `CreateOperationDefinition`. In case the filter is bound implicitly, this method is invoked for each operation.
+TODO: I think there is an issue with AllowNotEndsWith.
+
+```csharp
+// We override this method for implicity binding
+protected override FilterOperationDefintion CreateOperationDefinition(
+ FilterOperationKind operationKind) =>
+ CreateOperation(operationKind).CreateDefinition();
+```
+
+For the implicit binding, we only need to add the methods `AllowFrom` and `AllowTo`.
+
+```csharp
+// The following to methods are for adding the filters explicitly
+public IDateTimeFilterOperationDescriptor AllowFrom() =>
+ GetOrCreateOperation(FilterOperationKind.GreaterThanOrEqual);
+
+public IDateTimeFilterOperationDescriptor AllowTo() =>
+ GetOrCreateOperation(FilterOperationKind.LowerThanOrEqual);
+
+// This is just a little helper that reduces code duplication
+private DateTimeFilterOperationDescriptor GetOrCreateOperation(
+ FilterOperationKind operationKind) =>
+ Filters.GetOrAddOperation(operationKind,
+ () => CreateOperation(operationKind));
+```
+
+All the methods described above call `CreateOperation`. This method creates the operation descriptor. The `FitlerOperation` that is created here, will also be available for the expression visitor.
+
+```csharp
+// This helper method creates the operation.
+private DateTimeFilterOperationDescriptor CreateOperation(
+ FilterOperationKind operationKind)
+ {
+ // This operation is also available in execution.
+ var operation = new FilterOperation(
+ typeof(DateTime),
+ Definition.Kind,
+ operationKind,
+ Definition.Property);
+
+ return DateTimeOffsetFilterOperationDescriptor.New(
+ Context,
+ this,
+ CreateFieldName(operationKind),
+ RewriteType(operationKind),
+ operation,
+ FilterConvention);
+ }
+```
+
+**Filter Operation**
+
+In this example; there are two filter operations _"form"_ and _"to"_. The configuration with a descriptor combines explicit and implicit binding. As a base class, you can use `FilterOperationDescriptorBase`.
+Here is the interface that is used in this example:
+
+```csharp
+public interface IDateTimeFilterOperationDescriptor
+ : IDescriptor
+ , IFluent
+ {
+ /// Define filter operations for another field.
+ IDateTimeFilterFieldDescriptor And();
+
+ /// Specify the name of the filter operation.
+ IDateTimeFilterOperationDescriptor Name(NameString value);
+
+ /// Specify the description of the filter operation.
+ IDateTimeFilterOperationDescriptor Description(string value);
+
+ /// Annotate the operation filter field with a directive.
+ IDateTimeFilterOperationDescriptor Directive(T directiveInstance)
+ where T : class;
+ IDateTimeFilterOperationDescriptor Directive()
+ where T : class, new();
+ IDateTimeFilterOperationDescriptor Directive(
+ NameString name,
+ params ArgumentNode[] arguments);
+ }
+```
+
+You can find the implementation of this interface here: //TODO link
+
+**Filter Type Extension**
+The last missing piece to complete the integration into Hot Chocolate is an extension of `FilterInputType`. This can again be done as a extension method.
+
+```csharp
+public IStringFilterFieldDescriptor Filter(
+ Expression> property)
+{
+ if (property.ExtractMember() is PropertyInfo p)
+ {
+ return Fields.GetOrAddDescriptor(p,
+ () => new StringFilterFieldDescriptor(Context, p));
+ }
+
+ throw new ArgumentException(
+ FilterResources.FilterInputTypeDescriptor_OnlyProperties,
+ nameof(property));
+}
+```
+
+//TODO Open this api
+
+---
+
+##### Case Study: Filters for NetTopologySuite
+
+**Situation**
+
+> **Note:** If you are searching for `NetTopologySuite`, they are already implemented. Have a look at//TODO LINK
+
+1. Implicit Binding: `[UseFiltering]` should automagically create `Point` and the corresponding "\_distance"
+2. Explicity Binding: `desc.Filter(x => x.Location).AllowDistance()`
+3. Expression Visitor: You want to directly filter on the database. You use EF Core.
+
+Things are different in this case, as there is no longer a 1:1 mapping of input type to method or property. Imagine you want to fetch all bakeries that are near you. In C# you would write something like `dbContext.Bakeries.Where(x => x.Location.Distance(me.Location) < 5)`. This cannot be translated to a _GraphQL_ input type directly.
+
+A _GraphQL_ query might look like this.
+
+```graphql
+{
+ bakeries(
+ where: { location: { distance: { from: { x: 32, y: 15 }, is_lt: 5 } } }
+ ) {
+ name
+ }
+}
+```
+
+_GraphQL_ input fields cannot have arguments. To work around this issue a data structure is needed that combines the filter payload and the operation. The input type for this example has the following structure.
+
+```csharp
+public class FilterDistance
+{
+
+ public FilterDistance(
+ FilterPointData from)
+ {
+ From = from;
+ }
+ /// contains the x and y coordinates.
+ public FilterPointData From { get; }
+
+ public double Is { get; set; }
+}
+```
+
+```graphql
+input FilterDistanceInput {
+ from: FilterPointDataInput!
+ is: Float
+ is_gt: Float
+ is_gte: Float
+ is_lt: Float
+ is_lte: Float
+ is_in: Float
+ is_not: Float
+ is_not_gt: Float
+ is_not_gte: Float
+ is_not_lt: Float
+ is_not_lte: Float
+ is_not_in: Float
+}
+```
+
+//TODO: Add skip / inopfield!
+
+Hot Chocolate would generate nested filters for the payload property "From" by default. This can be avoided by declaring the field as input payload.
+
+```csharp
+public class DistanceFilterType
+ : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.Input(x => x.From);
+ descriptor.Filter(x => x.Is);
+ }
+}
+```
+
+**Convention & Implicit Factory & Type Descriptor**
+
+The configuration of the convention, the implicit type factory and the descirptors are very similar to the the two examples before. To not bloat the documentation with duplication we just refere to these two examples and to the reference implementation here //TODO LINK
+
+---
+
+## Translating Filters
+
+Hot Chocolate can translate incoming filters requests directly onto collections or even on to the database. In the default implementation, the output of this translation is a Linq expression that can be applied to `IQueryable` and `IEnumerable`. You can choose to change the expression that is generated or can even create custom output. Hot Chocolate is using visitors to translate input objects.
+
+[Learn more about visitors here](/docs/hotchocolate/v12/api-reference/visitors).
+
+### Expression Filters
+
+Filter conventions make it easier to change how an expression should be generated. There are three different extension points you can use to change the behavior of the expression visitor. You do not have to worry about the visiting of the input object itself.
+
+##### Describe the Expression Visitor
+
+The expression visitor descriptor is accessible through the filter convention. By calling `UseExpressionVisitor` on the convention descriptor you gain access. The expression visitor has the default set of expressions preconfigured.
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(
+ IFilterConventionDescriptor descriptor)
+ {
+ descriptor.UseExpressionVisitor()
+ }
+}
+```
+
+The descriptor provides a fluent interface that is very similar to the one of the convention descriptor itself. You have to specify what _operation_ on which _filter kind_ you want to configure. You can drill with `Kind` and `Operation` and go back up by calling `And()`:
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(
+ IFilterConventionDescriptor descriptor)
+ {
+ descriptor
+ .UseExpressionVisitor()
+ .Kind(FilterKind.String)
+ .Operation(FilterKind.Equals)
+ .And()
+ .And()
+ .Kind(FilterKind.Comparable)
+ .Operation(FilterKind.In)
+ }
+}
+```
+
+**Visitation Flow**
+
+The expression visitor starts as any other visitor at the node you pass in. Usually, this is the node object value node of the filter argument. It then starts the visitation. Every time the visitor _enters_ or _leaves_ an object field, it looks for a matching configuration. If there is no special _enter_ behavior of a field, the visitor generates the expression for the combination of _kind_ and _operation_.
+
+The next two paragraphs show how the algorithm works in detail.
+
+_Enter_
+
+On _entering_ a field, the visitor tries to get a `FilterFieldEnter` delegate for the `FilterKind` of the current field. If a delegate was found, executed, and the execution return true, the `Enter` method returns the _action_ specified by the delegate. In all other cases, the visitor tries to execute an `OperationHandler` for the combination `FilterKind` and `OperationKind`. If the handler returns true, the expression returned by the handler is added to the context.
+
+1. Let _field_ be the field that is visited
+1. Let _kind_ be the `FilterKind` of _field_
+1. Let _operation_ be the `FilterOperationKind` of _field_
+1. Let _convention_ be the `FilterConvention` used by this visitor
+1. Let _enterField_ be the `FilterFieldEnter` delegate for _kind_ on _convention_
+1. If _enterField_ is not null:
+ 1. Let _action_ be the visitor action of _enterField_
+ 1. If _enterField_ returns true:
+ 1. **return** _action_
+1. Let _operationHander_ be the `FilterOperationHandler` delegate for (_kind_, _operation_) on _convention_
+1. If _operationHandler_ is not null:
+ 1. Let _expression_ be the expression generated by _operationHandler_
+ 1. If _enterField_ returns true:
+ 1. enqueue _expression_
+1. **return** `SkipAndLeave`
+
+_Leave_
+
+On _entering_ a field, the visitor tries to get and execute a `FilterFieldLeave` delegate for the `FilterKind` of the current field.
+
+1. Let _field_ be the field that is visited
+1. Let _kind_ be the `FilterKind` of _field_
+1. Let _operation_ be the `FilterOperationKind` of _field_
+1. Let _convention_ be the `FilterConvention` used by this visitor
+1. Let _leaveField_ be the `FilterFieldLeave` delegate for _kind_ on _convention_
+1. If _leaveField_ is not null:
+ 1. Execute _leaveField_
+
+**Operations**
+
+The operation descriptor provides you with the method `Handler`. With this method, you can configure, how the expression for the _operation_ of this filter _kind_ is generated. You have to pass a delegate of the following type:
+
+```csharp
+public delegate bool FilterOperationHandler(
+ FilterOperation operation,
+ IInputType type,
+ IValueNode value,
+ IQueryableFilterVisitorContext context,
+ [NotNullWhen(true)]out Expression? result);
+```
+
+This delegate might seem intimidating first, but it is not bad as it looks. If this delegate `true` the `out Expression?` is enqueued on the filters. This means that the visitor will pick it up as it composes the filters.
+
+| Parameter | Description |
+| ---------------------------------------- | --------------------------------------- |
+| `FilterOperation operation` | The operation of the current field |
+| `IInputType type` | The input type of the current field |
+| `IValueNode value` | The AST value node of the current field |
+| `IQueryableFilterVisitorContext context` | The context that builds up the state |
+| `out Expression? result` | The generated expression |
+
+Operations handlers can be configured like the following:
+
+```csharp {10,13}
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(
+ IFilterConventionDescriptor descriptor)
+ {
+ descriptor
+ .UseExpressionVisitor()
+ .Kind(FilterKind.String)
+ .Operation(FilterKind.Equals)
+ .Handler(YourVeryOwnHandler.HandleEquals)
+ .And()
+ .Operation(FilterKind.NotEquals)
+ .Handler(YourVeryOwnHandler.HandleNotEquals)
+ }
+}
+```
+
+TODO: add example
+
+**Kind**
+
+There are two extension points on each _filter kind_. You can alter the _entering_ of a filter and the _leaving_.
+
+**Enter**
+You can configure the entering with the following delegate:
+
+```csharp
+public delegate bool FilterFieldEnter(
+ FilterOperationField field,
+ ObjectFieldNode node,
+ IQueryableFilterVisitorContext context,
+ [NotNullWhen(true)]out ISyntaxVisitorAction? action);
+```
+
+If this field returns _true_ the filter visitor will continue visitation with the specified _action_ in the out parameter `action`. [Check out the visitor documentation for all possible actions](http://addlinkshere).
+If the field does not return true and a visitor action, the visitor will continue and search for a _operation handler_. After this, the visitor will continue with `SkipAndLeave`.
+
+| Parameter | Description |
+| ---------------------------------------- | ------------------------------------ |
+| `FilterOperationField field` | The current field |
+| `ObjectFieldNode node` | The object node of the current field |
+| `IQueryableFilterVisitorContext context` | The context that builds up the state |
+| `out ISyntaxVisitorAction? action` | The visitor action |
+
+**Leave**
+You can configure the entering with the following delegate:
+
+```csharp
+public delegate void FilterFieldLeave(
+ FilterOperationField field,
+ ObjectFieldNode node,
+ IQueryableFilterVisitorContext context);
+```
+
+| Parameter | Description |
+| ---------------------------------------- | ------------------------------------ |
+| `FilterOperationField field` | The current field |
+| `ObjectFieldNode node` | The object node of the current field |
+| `IQueryableFilterVisitorContext context` | The context that builds up the state |
diff --git a/website/src/docs/hotchocolate/v12/api-reference/index.md b/website/src/docs/hotchocolate/v12/api-reference/index.md
new file mode 100644
index 00000000000..4a75b5e9079
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/index.md
@@ -0,0 +1,7 @@
+---
+title: "Overview"
+---
+
+> We are still working on the documentation for Hot Chocolate so help us by finding typos, missing things or write some additional docs with us.
+
+The API Reference provides deep dives into specific subjects like [ASP.NET Core](/docs/hotchocolate/v12/api-reference/aspnetcore) configuration or how you can [extend filtering](/docs/hotchocolate/v12/api-reference/extending-filtering).
diff --git a/website/src/docs/hotchocolate/v12/api-reference/language.md b/website/src/docs/hotchocolate/v12/api-reference/language.md
new file mode 100644
index 00000000000..5282926684f
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/language.md
@@ -0,0 +1,86 @@
+---
+title: "Language"
+---
+
+# Abstract Syntax Tree (AST)
+
+Hot Chocolate seems to focus solely around `ObjectType`, `InputType` et al. These types work as an interface to configure the _GraphQL_ schema. This schema is used to parse and validate incoming requests. Under the hood, every `query`, `mutation` or `subscription` request is parsed into a so-called abstract syntax tree. Each node of this tree denotes a part of the incoming _GraphQL_ query.
+
+```graphql
+query Users {
+ userName
+ address {
+ street
+ nr
+ }
+}
+```
+
+```mermaid
+graph TD;
+ OperationDefinitionNode --> s1["SelectionSetNode"]
+ s1["SelectionSetNode"] --> id5["FieldNode (userName)"]
+ s1["SelectionSetNode"] --> id1["FieldNode (address)"]
+ id1["FieldNode (address)"] --> s2["SelectionSetNode"]
+ s2["SelectionSetNode"] --> id3["FieldNode (street)"]
+ s2["SelectionSetNode"] --> id4["FieldNode (nr)"]
+
+```
+
+---
+
+# Syntax Node
+
+Every node in a syntax tree implements `ISyntaxNode`.
+
+> 💡 The `ToString` method of a syntax node prints the corresponding _GraphQL_ syntax.
+
+This interface defines the `NodeKind` of the node.
+
+**Node Kinds:**
+
+| Name | Description (Spec Link) | Context | Example |
+| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ------------------------------- |
+| Name | [All names. e.g. Field, Argument ... ](http://spec.graphql.org/June2018/#sec-Names) | Both | foo |
+| NamedType | [Denotes a reference to a type](https://spec.graphql.org/June2018/#NamedType) | Both | Foo |
+| ListType | [Definition of a list](https://spec.graphql.org/June2018/#ListType) | Both | \[Foo] |
+| NonNullType | [Definition of type that cannot be null](https://spec.graphql.org/June2018/#NonNullType) | Both | Foo! |
+| Argument | [Representation of an argument. Has a _Name_ and a _Value_](https://spec.graphql.org/June2018/#sec-Language.Arguments) | Both | foo: "bar" |
+| Directive | [Denotes a directive ](https://spec.graphql.org/June2018/#sec-Language.Directives) | Query | @foo |
+| Document | [Describes a complete file or request a _GraphQL_ service operates on.](http://spec.graphql.org/June2018/#sec-Language.Document) | Query (out) | |
+| OperationDefinition | [Describes a graphql operation like `query` `mutation` or `subscription`](http://spec.graphql.org/June2018/#sec-Language.Document) | Query (out) | query Foo {} |
+| VariableDefinition | [The variables defined by an operation](http://spec.graphql.org/June2018/#VariableDefinitions) | Query (out) | (\$foo: String) |
+| Variable | [A variable](https://spec.graphql.org/June2018/#sec-Language.Variables) | Query (out) | \$foo |
+| SelectionSet | [specifies a selection of _Field_, _FragmentSpread_ or _InlineFragment_](http://spec.graphql.org/June2018/#sec-Selection-Sets) | Query (out) | {foo bar} |
+| Field | [Describes a field as a part of a selection set](http://spec.graphql.org/June2018/#sec-Language.Fields) | Query (out) | foo |
+| FragmentSpread | [Denotes a spread of a `FragemntDefinition`](https://spec.graphql.org/June2018/#FragmentSpread) | Query (out) | ...f1 |
+| InlineFragment | [Denotes an inline fragment](https://spec.graphql.org/June2018/#sec-Inline-Fragments) | Query (out) | ... on Foo { bar} |
+| FragmentDefinition | [Defines the definition of a fragment](https://spec.graphql.org/June2018/#FragmentDefinition) | Query (out) | fragment f1 on Foo {} |
+| IntValue | [Denotes a `int` value](https://spec.graphql.org/June2018/#sec-Int-Value) | Query (in) | 1 |
+| StringValue | [ Denotes a `string` value](https://spec.graphql.org/June2018/#sec-String-Value) | Query (in) | "bar" |
+| BooleanValue | [Denotes a `boolean` value ](https://spec.graphql.org/June2018/#sec-Boolean-Value) | Query (in) | true |
+| NullValue | [Denotes a `null` value ](https://spec.graphql.org/June2018/#sec-Null-Value) | Query (in) | null |
+| EnumValue | [Denotes a `enum` value ](https://spec.graphql.org/June2018/#sec-Enum-Value) | Query (in) | FOO |
+| FloatValue | [Denotes a _Float_ value](https://spec.graphql.org/June2018/#sec-Float-Value) | Query (in) | 0.2 |
+| ListValue | [Denotes a _List_ value](https://spec.graphql.org/June2018/#sec-List-Value) | Query (in) | \["string"] |
+| ObjectValue | [Denotes a _ObjectValue_ value ](https://spec.graphql.org/June2018/#sec-Input-Object-Values) | Query (in) | {foo: "bar" } |
+| ObjectField | [Denotes a field of am input object type](https://spec.graphql.org/June2018/#ObjectField) | Query (in) | foo: "bar" |
+| SchemaDefinition | [Definition of a schema](https://spec.graphql.org/June2018/#sec-Schema) | Schema | schema {} |
+| OperationTypeDefinition | [This defines one of the root operations `Query`, `Mutation` or `Subscription` on the schema-definiton](https://spec.graphql.org/June2018/#RootOperationTypeDefinition) | Schema | query:FooQuery |
+| ScalarTypeDefinition | [Definition of a scalar ](https://spec.graphql.org/June2018/#sec-Scalars) | Schema | scalar JSON |
+| ObjectTypeDefinition | [Definition of an object type](https://spec.graphql.org/June2018/#sec-Objects) | Schema | type Foo{} |
+| FieldDefinition | [Definition of a field](https://spec.graphql.org/June2018/#FieldDefinition) | Schema | bar:String |
+| InputValueDefinition | [Definition of a input value of an argument](https://spec.graphql.org/June2018/#sec-Field-Arguments) | Schema | x: Float |
+| InterfaceTypeDefinition | [Definition of an interface](https://spec.graphql.org/June2018/#sec-Interfaces) | Schema | interface NamedEntity {} |
+| UnionTypeDefinition | [Definition of an union](https://spec.graphql.org/June2018/#sec-Unions) | Schema | union Ex = Foo \| Bar |
+| EnumTypeDefinition | [Definition of an enum](https://spec.graphql.org/June2018/#sec-Enums) | Schema | enum Foo {BAR} |
+| EnumValueDefinition | [Definition of an enum value](https://spec.graphql.org/June2018/#sec-Enum) | Schema | BAR |
+| InputObjectTypeDefinition | [Definition of an input type definition](https://spec.graphql.org/June2018/#sec-Input-Objects) | Schema | input FooInput {} |
+| SchemaExtension | [Definition of a schema extension](https://spec.graphql.org/June2018/#sec-Schema-Extension) | Schema | extend schema {} |
+| ScalarTypeExtension | [Definition of a scalar extension](https://spec.graphql.org/June2018/#sec-Scalar-Extensions) | Schema | extend scalar Foo @bar |
+| ObjectTypeExtension | [Definition of an object type extension](https://spec.graphql.org/June2018/#sec-Object-Extensions) | Schema | extend type Foo { name} |
+| InterfaceTypeExtension | [Definition of an interface type extension](https://spec.graphql.org/June2018/#sec-Interface-Extensions) | Schema | extend interface NamedEntity {} |
+| UnionTypeExtension | [Definition of an union type extension](https://spec.graphql.org/June2018/#sec-Union-Extensions) | Schema | extend union Ex = Foo{} |
+| EnumTypeExtension | [Definition of an enum type extension](https://spec.graphql.org/June2018/#sec-Enum-Extensions) | Schema | extend enum foo{} |
+| InputObjectTypeExtension | [Definition of an input types](https://spec.graphql.org/June2018/#sec-Input-Object-Extensions) | Schema | input foo {} |
+| DirectiveDefinition | [Definition of a directive](https://spec.graphql.org/June2018/#sec-Type-System.Directives) | Schema | directive @foo on |
diff --git a/website/src/docs/hotchocolate/v12/api-reference/migrate-from-10-to-11.md b/website/src/docs/hotchocolate/v12/api-reference/migrate-from-10-to-11.md
new file mode 100644
index 00000000000..82a8258b77e
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/migrate-from-10-to-11.md
@@ -0,0 +1,792 @@
+---
+title: Migrate from Hot Chocolate GraphQL server 10 to 11
+---
+
+This guide will walk you through the manual migration steps to get you Hot Chocolate GraphQL server to version 11.
+
+As a general preparation, we recommend removing all HotChocolate.\* package references from your project. Then start by adding the `HotChocolate.AspNetCore` package. The server package now contains most of the needed packages.
+
+When do I need to add other Hot Chocolate packages explicitly?
+
+We have now added the most common packages to the Hot Chocolate core. But there are certain areas where we still need to add some additional packages.
+
+| Package | Topic |
+| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| HotChocolate.AspNetCore.Authorization | The authorization package adds the authorization directive and integrates with Microsoft Authorization Policies |
+| HotChocolate.Data | The new data package represents our integration with all kinds of data sources. This package provides the fundamentals for filtering, sorting, and projection logic. |
+| HotChocolate.Types.Spatial | This package provides GeoJson spatial types. |
+| HotChocolate.Data.Spatial | The package integrates the spatial types with the data package to allow for spatial filtering, sorting, and projections. |
+| HotChocolate.Subscriptions.Redis | The in-memory subscription provider, is now integrated by default. To have an integration with Redis, you need to add this package. |
+| HotChocolate.PersistedQueries.FileSystem | This package provides a persisted query storage for the file system. |
+| HotChocolate.PersistedQueries.Redis | This package provides a persisted query storage for Redis. |
+
+# ASP.NET Core
+
+One of the main focuses of version 11 was to create a new configuration API that brings all our builders together into one unified API. This also means that we had to introduce breaking changes to the way we
+configure schemas.
+
+After you have cleaned up your packages, head over to the `Startup.cs` to start with the new configuration API migration.
+
+## ConfigureServices
+
+In your `Startup.cs` head over to the `ConfigureServices` methods.
+The configuration of a schema has slightly changed, and the new configuration API has replaced the `SchemaBuilder`.
+
+We now start with `AddGraphQLServer` to define a new GraphQL server, `AddGraphQLServer`, returns the new `IRequestExecutorBuilder` that lets us apply all the configuration methods that used to be on the `SchemaBuilder`, `StitchingBuilder` and the `QueryExecutionBuilder`.
+
+**Old:**
+
+```csharp
+services.AddGraphQL(sp =>
+ SchemaBuilder.New()
+ .AddServices(sp)
+ .AddQueryType()
+ .AddMutationType()
+ ...
+ .Create());
+```
+
+**New:**
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddMutationType()
+ ...
+```
+
+If you were using the `QueryRequestBuilder` to configure request options or change the request pipeline, you need to add those things to the configuration chain of the ```IRequestExecutorBuilder`.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddMutationType()
+ ...
+ .ModifyRequestOptions(o => o.ExecutionTimeout = TimeSpan.FromSeconds(180));
+```
+
+## Configure
+
+After migrating the schema configuration, the next area that has fundamentally changed is the schema middleware.
+
+Hot Chocolate server now embraces the new endpoint routing API from ASP.NET core and with that brings a lot of new features. Head over [here](/docs/hotchocolate/v12/api-reference/aspnetcore) to read more about the ASP.NET Core integration.
+
+**Old:**
+
+```csharp
+app.UseGraphQL();
+```
+
+**New:**
+
+```csharp
+app.UseRouting();
+
+// routing area
+
+app.UseEndpoints(x => x.MapGraphQL());
+```
+
+## Request Interceptor
+
+The query request interceptor was reworked and we renamed it to `IHttpRequestInterceptor`.
+
+```csharp
+public interface IHttpRequestInterceptor
+{
+ ValueTask OnCreateAsync(
+ HttpContext context,
+ IRequestExecutor requestExecutor,
+ IQueryRequestBuilder requestBuilder,
+ CancellationToken cancellationToken);
+}
+```
+
+**Old:**
+
+```csharp
+services.AddQueryRequestInterceptor(
+ (context, builder, ct) =>
+ {
+ // your code
+ });
+```
+
+**New:**
+
+```csharp
+services.AddGraphQLServer()
+ ...
+ .AddHttpRequestInterceptor(
+ (context, executor, builder, ct) =>
+ {
+ // your code
+ });
+```
+
+You can also extend `DefaultHttpRequestInterceptor` and inject it like the following.
+
+```csharp
+services.AddGraphQLServer()
+ ...
+ .AddHttpRequestInterceptor();
+```
+
+> A request interceptor is a service that is used by all hosted schemas.
+
+## Entity Framework Serial Execution
+
+The serial execution for Entity Framework compatibility is gone. If you use Entity Framework Core we recommend using version 5 and the new context factory in combination with context pooling. This allows the execution engine to execute in parallel and still be memory efficient since context objects are pooled.
+
+Another variant here is to use our scoped service feature that scopes services for the resolver pipeline. This is explained in our GraphQL Workshop project.
+
+https://github.com/ChilliCream/graphql-workshop
+
+# Schema / Resolvers
+
+### Field ordering
+
+Hot Chocolate 11 follows the spec and returns the fields in the order they were defined. This feature
+makes migrations harder because the schema snapshot looks different compared to version 11. You can change this behavior with the following setting.
+
+```csharp
+ builder.ModifyOptions(x => x.SortFieldsByName = true)
+```
+
+## DataLoaders
+
+With Hot Chocolate server 11, we have embraced the new DataLoader spec version 2. With that, we have decoupled the scheduler from the DataLoader itself, meaning you now have to pass on the `IBatchScheduler` to the base implementation of the DataLoader.
+Apart from that, DataLoader now uses `ValueTask` instead of `Task` when doing async work.
+
+If you were adding the `DataLoaderRegistry` to the services, remove that code since `service.AddDataLoaderRegistry` is no longer needed.
+
+**Old:**
+
+```csharp
+public class FooDataLoader : DataLoaderBase
+{
+ private readonly IFooRepository _fooRepository;
+
+ public FooDataLoader(IFooRepository fooRepository)
+ {
+ _fooRepository = fooRepository;
+ }
+
+
+ protected override async Task>> FetchAsync(
+ IReadOnlyList keys,
+ CancellationToken cancellationToken)
+ {
+ ....
+ }
+}
+```
+
+**New:**
+
+```csharp
+public class FooDataLoader : DataLoaderBase
+{
+ private readonly IFooRepository _fooRepository;
+
+ public FooDataLoader(
+ // ▼
+ IBatchScheduler scheduler,
+ IFooRepository fooRepository)
+ : base(scheduler)
+ {
+ _fooRepository = fooRepository;
+ }
+
+
+ // ▼
+ protected override async ValueTask>> FetchAsync(
+ IReadOnlyList keys,
+ CancellationToken cancellationToken)
+ {
+
+ ....
+}
+```
+
+## Node Resolver
+
+With version 11, we have reworked how Relay node types are defined. Furthermore, we added pure code-first (annotation-based) support.
+
+**Old:**
+
+```csharp
+descriptor
+ .AsNode()
+ .IdField(d => d.Id)
+ .NodeResolver(async (ctx, id) => await ctx
+ .DataLoader()
+ .LoadAsync(id, ctx.RequestAborted))
+```
+
+**New:**
+
+The following example essentially aligns very closely to the old variant.
+
+```csharp
+descriptor
+ .ImplementsNode()
+ .IdField(d => d.Id)
+ .ResolveNode(async (ctx, id) => await ctx
+ .DataLoader()
+ .LoadAsync(id, ctx.RequestAborted))
+```
+
+But, we can now also use an external resolver like with standard resolvers. This allows us to write better testable code that takes advantage of the method parameter injection we use in everyday resolvers.
+
+```csharp
+descriptor
+ .ImplementsNode()
+ .IdField(d => d.Id)
+ .ResolveNodeWith(t => t.GetNodeAsync(default, default));
+```
+
+But we can go even further now with pure code-first (annotation-based) support. By just annotating the entity with the `NodeAttribute`, we essentially told the schema builder that this is a node. The type initialization can then try to infer the node resolver directly from the type.
+
+```csharp
+[Node]
+public class MyEntity
+{
+ public string Id { get; set; }
+
+ public async Task GetAsync(....)
+ {
+ ....
+ }
+}
+```
+
+Often, however, we want the repository logic decoupled from our domain object/entity. In this case, we can specify the entity resolver type.
+
+```csharp
+[Node(NodeResolverType = typeof(MyEntityResolver))]
+public class MyEntity
+{
+ public string Id { get; set; }
+}
+
+public class MyEntityResolver
+{
+ public async Task GetAsync(....)
+ {
+ ....
+ }
+}
+```
+
+There are more variants possible, but to give an impression of the new convenience and flexibility around nodes. As a side note, if you do not want the node attribute on the domain objects, you can also now add your very own attribute or interface to mark this and rewrite that in the schema building process to the `NodeAttribute`.
+
+## Pagination
+
+The first thing to note around pagination is that we listened to a lot of feedback and have removed the `PaginationAmountType`.
+
+Moreover, we have introduced new PagingOptions, which can be set with the new configuration API on the schema level. With the new options, you can configure the `MaxPageSize`, `DefaultPageSize` and whether the total count shall be included `IncludeTotalCount`.
+
+```csharp
+builder.SetPagingOptions(
+ new PagingOptions()
+ {
+ MaxPageSize = searchOptions.PaginationAmount,
+ DefaultPageSize = searchOptions.PaginationAmount,
+ IncludeTotalCount = true
+ });
+```
+
+Further, you can override the paging option on the resolver level.
+
+```csharp
+[UsePaging(MaxPageSize = 100)]
+```
+
+```csharp
+descriptor.Field(...).UsePaging(maxPageSize = 100)...
+```
+
+## Projections
+
+The selection middleware, that was available in `HotChocolate.Types.Selections` was replaced by the projection middleware from `HotChocolate.Data`.
+
+**Old:**
+
+```csharp
+descriptor.Field(...).UseSelection()...
+```
+
+**New:**
+
+```csharp
+descriptor.Field(...).UseProjection()...
+```
+
+Similarly, the attribute `[UseSelection]` was replaced by `[UseProjection]`.
+
+To use projections with your GraphQL endpoint you have to register it on the schema:
+
+```csharp
+services.AddGraphQLServer()
+ // Your schema configuration
+ .AddProjections();
+```
+
+## Enum Type
+
+Hot Chocolate server 11 now follows the spec recommendation with the new enum name conventions and formats the enum values by default as UPPER_SNAIL_CASE.
+
+To avoid breaking changes to your schema, you will have to override the naming convention:
+
+**Configuration:**
+
+```csharp
+ builder
+ .AddConvention(new CompatibilityNamingConvention())
+```
+
+**Convention:**
+
+```csharp
+ public class CompatibilityNamingConvention
+ : DefaultNamingConventions
+ {
+ public override NameString GetEnumValueName(object value)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ return value.ToString().ToUpperInvariant();
+ }
+ }
+```
+
+## IResolverContext.Source
+
+The source result stack was removed from the resolver context for performance reasons. If you need such a functionality, you can write a middleware that aggregates the resulting path on the scoped context.
+
+**Old:**
+
+```csharp
+ public class FooType : ObjectType
+ {
+ private static readonly object _empty = new object();
+
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("bar")
+ .Type>()
+ .Resolver(_empty);
+ }
+ }
+
+ public class BarType : ObjectType
+ {
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("baz")
+ .Type()
+ .Resolve(ctx =>
+ {
+ Foo foo = (Foo)ctx.Source.Pop().Peek();
+ return foo.Baz;
+ });
+ }
+ }
+
+```
+
+**New:**
+
+```csharp
+ public class FooType : ObjectType
+ {
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("bar")
+ .Type>()
+ .Resolve(
+ ctx =>
+ {
+ ctx.ScopedContextData =
+ ctx.ScopedContextData.SetItem(nameof(Foo), ctx.Parent());
+ return new object();
+ });
+ }
+ }
+
+ public class BarType : ObjectType
+ {
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("baz")
+ .Type()
+ .Resolve(
+ ctx =>
+ {
+ if (ctx.ScopedContextData.TryGetValue(nameof(Foo), out object? potentialFoo) &&
+ potentialFoo is Foo foo)
+ {
+ return foo.Baz;
+ }
+
+ throw new GraphQLException(
+ ErrorBuilder.New()
+ .AddLocation(ctx.Field.SyntaxNode)
+ .SetMessage("Foo was not pushed down.")
+ .SetPath(ctx.Path)
+ .Build());
+ });
+ }
+ }
+```
+
+## Authorization
+
+If you use authorization, you need to add a package reference to `HotChocolate.AspNetCore.Authorization`.
+
+**Old:**
+
+```csharp
+ builder.AddAuthorizeDirectiveType()
+```
+
+**New:**
+
+```csharp
+ builder.AddAuthorization()
+```
+
+## TypeBinding
+
+We have renamed the binding method from `BindClrType` to `BindRuntimeType` to make it more clear what it does.
+
+**Old:**
+
+```csharp
+ builder.BindClrType()
+```
+
+**New:**
+
+```csharp
+ builder.BindRuntimeType()
+```
+
+## FieldMiddleware
+
+Since all configuration APIs were integrated into one, we needed to make it more specific for what a middleware is defined. `UseField` defines a middleware that is applied to the resolver pipeline / field pipeline whereas `UseRequest` defines a middleware that is defined for the request processing.
+
+**Old:**
+
+```csharp
+ builder.Use()
+```
+
+**New:**
+
+```csharp
+ builder.UseField()
+```
+
+# Stitching
+
+The schema stitching configuration API has been completely integrated into the new configuration API. This means that a Gateway is nothing more than a GraphQL schema, which will make it easier for new users. However, you will need to completely rewire your stitching configuration.
+
+## Configuration
+
+The stitching builder no longer exists in version 11 and you need to use the new configuration API to configure your gateway.
+
+**Old:**
+
+```csharp
+ services.AddStitchedSchema(x => ....);
+```
+
+**New:**
+
+```csharp
+ services.AddGraphQLServer()....
+```
+
+### AddSchemaFromHttp
+
+Registering a remote schema has slightly changed in version 11 to make it more clear that we are adding a remote schema into the local gateway schema. Removing, root types and importing a remote schema can be done in one go now.
+
+**Old:**
+
+```csharp
+ builder.AddSchemaFromHttp("SomeSchema").IgnoreRootTypes("SomeSchema");
+```
+
+**New:**
+
+```csharp
+ builder.AddRemoteSchema("SomeSchema", ignoreRootTypes: true);
+```
+
+## AddSchemaConfiguration
+
+In version 11 it is now much easier to configure the gateway schema.
+
+**Old:**
+
+```csharp
+ services.AddStitchedSchema(x => x.AddSchemaConfiguration(y => y.RegisterType()));
+```
+
+**New:**
+
+```csharp
+ services
+ .AddGraphQLServer()
+ .AddType();
+```
+
+## IgnoreField
+
+The order of the parameters in ignore field and ignore type has changed since we moved optional parameters to the end.
+
+**Old:**
+
+```csharp
+ services.AddStitchedSchema(x => x.IgnoreField("SchemaName", "TypeName, "FieldName"));
+```
+
+**New:**
+
+```csharp
+ services
+ .AddGraphQLServer()
+ .IgnoreField("TypeName, "FieldName", "SchemaName")
+```
+
+## SetExecutionOptions
+
+Execution options can now be configured on the root schema directly like for any other schema:
+
+**Old:**
+
+```csharp
+ services.AddStitchedSchema(
+ x => x.SetExecutionOptions(
+ new QueryExecutionOptions
+ {
+ TracingPreference = TracingPreference.OnDemand
+ }));
+```
+
+**New:**
+
+```csharp
+ services
+ .AddGraphQLServer()
+ .ModifyRequestOptions(x => x.TracingPreference = TracingPreference.OnDemand);
+```
+
+## Configuring a downstream schema
+
+In case you want to configure a downstream schema, you can now just use the new configuration API since all downstream schemas have an in-memory representation.
+
+```csharp
+ services
+ .AddGraphQLServer()
+ .AddRemoteSchema("SomeSchema");
+
+ services
+ .AddGraphQL("SomeSchema")
+ .AddType(new IntType("SpecialIntegerType"));
+```
+
+## PaginationAmount
+
+The `PaginationAmount` scalar was removed since it caused a lot of issues with clients and only provided limited benefit. The arguments `first` and `last` use now `Int` as a type. To avoid breaking schemas on a stitched schema, you can add a rewriter that rewrites all
+`first: Int` and `last: Int` on a connection to `first: PaginationAmount` and `last: PaginationAmount`.
+You also have to make sure that you register a new `IntType` on the root schema and rewrite all
+downstream schemas.
+
+**Configuration:**
+
+```csharp
+ services
+ .AddGraphQLServer()
+ .AddRemoteSchema("SomeSchema")
+ .ConfigureSchema(x =>
+ x.AddType(new IntType())
+ .AddType(new IntType("PaginationAmount")))
+ .AddMergedDocumentRewriter(
+ d => (DocumentNode)new PagingAmountRewriter().Rewrite(d, null));
+
+ services
+ .AddGraphQL("SomeSchema")
+ .ConfigureSchema(x =>
+ x.AddType(new IntType())
+ .AddType(new IntType("PaginationAmount")));
+```
+
+**PagingAmountRewriter:**
+
+```csharp
+ internal class PagingAmountRewriter : SchemaSyntaxRewriter
+ {
+ protected override FieldDefinitionNode RewriteFieldDefinition(
+ FieldDefinitionNode node,
+ object? context)
+ {
+ if (node.Type.NamedType().Name.Value.EndsWith("Connection") &&
+ (node.Arguments.Any(
+ t => t.Name.Value.EqualsOrdinal("first") &&
+ t.Type.NamedType().Name.Value.EqualsOrdinal("Int"))
+ || node.Arguments.Any(
+ t => t.Name.Value.EqualsOrdinal("last") &&
+ t.Type.NamedType().Name.Value.EqualsOrdinal("Int"))
+ ))
+ {
+ var arguments = node.Arguments.ToList();
+
+ InputValueDefinitionNode first =
+ arguments.FirstOrDefault(t => t.Name.Value.EqualsOrdinal("first"));
+
+ InputValueDefinitionNode last =
+ arguments.FirstOrDefault(t => t.Name.Value.EqualsOrdinal("last"));
+
+ if (first != null) arguments[arguments.IndexOf(first)] = first.WithType(RewriteType(first.Type, "PaginationAmount"));
+
+ if (last != null) arguments[arguments.IndexOf(last)] = last.WithType(RewriteType(last.Type, "PaginationAmount"));
+
+ node = node.WithArguments(arguments);
+ }
+
+ return base.RewriteFieldDefinition(node, context);
+ }
+
+ private static ITypeNode RewriteType(ITypeNode type, NameString name)
+ {
+ if (type is NonNullTypeNode nonNullType)
+ {
+ return new NonNullTypeNode(
+ (INullableTypeNode)RewriteType(nonNullType.Type, name));
+ }
+
+ if (type is ListTypeNode listType)
+ {
+ return new ListTypeNode(RewriteType(listType.Type, name));
+ }
+
+ return new NamedTypeNode(name);
+ }
+ }
+
+ internal static class StringExtensions
+ {
+ public static bool EqualsOrdinal(this string value, string other) =>
+ string.Equals(value, other, StringComparison.Ordinal);
+ }
+```
+
+## Batch responses
+
+In v10, responses to batched operations were returned as a JsonArray. In v11 the default is to return MultiPartChunked responses. To switch back to JsonArray, configure the HttpResult serializer as follows:
+
+```csharp
+services.AddHttpResultSerializer(
+ batchSerialization: HttpResultSerialization.JsonArray
+);
+```
+
+# Testing
+
+We have added a couple of test helpers to make the transition to the new configuration API easier.
+
+## Schema Snapshot Tests
+
+**Old:**
+
+```csharp
+ SchemaBuilder.New()
+ .AddQueryType()
+ .Create()
+ .ToString()
+ .MatchSnapshot();
+```
+
+**New:**
+
+```csharp
+ ISchema schema =
+ await new ServiceCollection()
+ .AddGraphQL()
+ .AddQueryType()
+ .BuildSchemaAsync();
+
+ schema.Print().MatchSnapshot();
+```
+
+## Request Tests
+
+**Old:**
+
+```csharp
+ IQueryExecutor executor =
+ SchemaBuilder.New()
+ .AddQueryType()
+ .Create()
+ .MakeExecutable();
+```
+
+**New:**
+
+```csharp
+ IRequestExecutor executor =
+ await new ServiceCollection()
+ .AddGraphQL()
+ .AddQueryType()
+ .BuildRequestExecutorAsync();
+
+ IExecutionResult result =
+ await executor.ExecuteAsync("{ __typename }");
+
+ result.ToJson().MatchSnapshot();
+```
+
+Or you can directly build and execute:
+
+```csharp
+ IExecutionResult result =
+ await new ServiceCollection()
+ .AddGraphQL()
+ .AddQueryType()
+ .ExecuteRequestAsync("{ __typename }");
+
+ result.ToJson().MatchSnapshot();
+```
+
+## DataLoader Testing
+
+Due to the changed constructor you now need to also create a scheduler for the dataloaders
+
+Old
+
+```csharp
+ FooDataLoader dataLoader = new FooDataLoader( fooRepoMock.Object);
+```
+
+New
+
+```csharp
+ var scheduler = new BatchScheduler();
+ FooDataLoader dataLoader = new FooDataLoader(
+ scheduler,
+ fooRepoMock.Object);
+```
+
+// TODO : Type Converter
diff --git a/website/src/docs/hotchocolate/v12/api-reference/migrate-from-11-to-12.md b/website/src/docs/hotchocolate/v12/api-reference/migrate-from-11-to-12.md
new file mode 100644
index 00000000000..f7aed60c37b
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/migrate-from-11-to-12.md
@@ -0,0 +1,323 @@
+---
+title: Migrate from Hot Chocolate GraphQL server 11 to 12
+---
+
+This guide will walk you through the manual migration steps to get your Hot Chocolate GraphQL server to version 12.
+
+# Resolvers
+
+We have reworked the resolver compiler and are now demanding that the `ParentAttribute` is used when an argument is referring to the parent object.
+This is done since in some cases people want to get the parent object which is the same runtime type as an argument value.
+
+**v11**
+
+```csharp
+public string MyResolver(Person parent, string additionalInput)
+{
+ // Code omitted for brevity
+}
+```
+
+**v12**
+
+```csharp
+public string MyResolver([Parent] Person parent, string additionalInput)
+{
+ // Code omitted for brevity
+}
+```
+
+# Scalars
+
+We changed some defaults around scalars. These new defaults can break your existing schema but are, in general, better for newcomers and align better with the overall GraphQL ecosystem. Of course, you can naturally opt out of these new defaults to preserve your current schema's integrity.
+
+## UUID
+
+We changed the name of the UUID scalar from `Uuid` to `UUID`. To maintain the old name, register the type manually like the following:
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddType(() => new UuidType("Uuid"));
+```
+
+Further, we changed the default serialization of UUID values from format `N` (`nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn`) to format `D` (`nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn`). While the format `N` saved a few payload characters, new users, in general, often had issues with that format and some other tooling. New users will now, by default, have a better experience when using non-ChilliCream tooling.
+
+To preserve the old format, you can directly provide the format in the scalar.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddType(() => new UuidType(defaultFormat: 'N'));
+```
+
+In order to fully preserve version 11 behavior do:
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddType(() => new UuidType("Uuid", defaultFormat: 'N'));
+```
+
+## URL
+
+We changed the name of the URL scalar from `Url` to `URL`. To maintain the old name, register the type manually like the following:
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddType(() => new UrlType("Url"));
+```
+
+# Pagination
+
+## ConnectionType
+
+In v12 we have removed the `ConnectionType` and `ConnectionType`.
+
+**v11**
+
+```csharp
+descriptor
+ .Field("users")
+ .UsePaging()
+ .Type>()
+ .Resolver(context =>
+ {
+ // Omitted code for brevity
+ });
+```
+
+**v12**
+
+```csharp
+descriptor
+ .Field("users")
+ .UsePaging()
+ .Resolver(context =>
+ {
+ // Omitted code for brevity
+ });
+```
+
+## Connection naming
+
+We have changed the way we infer the name for the connection type when using cursor-based pagination. By default, the connection name is now inferred from the field name instead of the type name.
+
+```sdl
+type Person {
+ friends: [Person]
+}
+```
+
+In version 11, we would have created a connection named `PersonConnection`.
+
+```sdl
+type Person {
+ friends(first: Int, last: Int, after: String, before: String): PersonConnection
+}
+```
+
+In version 12, we now will infer the connection name as `FriendsConnection`.
+
+```sdl
+type Person {
+ friends(first: Int, last: Int, after: String, before: String): FriendsConnection
+}
+```
+
+To keep your schema stable when you migrate, you can switch the behavior back to how you did in version 11.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .SetPagingOptions(new PagingOptions{ InferConnectionNameFromField = false })
+ ...
+```
+
+Moreover, you now can explicitly define the connection name per field.
+
+```csharp
+public class Person
+{
+ [UsePaging(ConnectionName = "Persons")]
+ public IQueryable GetFriends() => ...
+}
+```
+
+[Reference](/docs/hotchocolate/v12/fetching-data/pagination#naming)
+
+## MongoDB Paging
+
+In version 11 we had the `UseMongoDbPagingAttribute` and the `UseMongoDbOffsetPagingAttribute`, which we removed with version 11. In version 12 you now can use the standard attributes `UsePagingAttribute` and `UseOffsetPagingAttribute`.
+
+To use these attributes with mongo, you need to register the mongo paging provider with your GraphQL configuration:
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddMongoDbPagingProviders()
+ ...
+```
+
+[Reference](/docs/hotchocolate/v12/fetching-data/pagination#providers)
+
+# Records
+
+With version 11, we added support for records and added the ability to infer attributes from parameters. This, in the end, leads to more errors than benefits. With version 12, we removed this feature. Use the official' property' keyword to write records in C# short-hand syntax when annotating properties.
+
+```csharp
+public record Foo([property: ID] string Id);
+```
+
+# Instrumentation
+
+We added more instrumentation events and generalized more how one can tap into our internal events. The class `DiagnosticEventListener` is now obsolete and replaced with `ExecutionDiagnosticEventListener`. This is due to new event listener classes like `DataLoaderDiagnosticEventListener`. Most virtual methods previously returning IActivityScope now return IDisposable.
+
+[Learn more about instrumentation](/docs/hotchocolate/v12/server/instrumentation)
+
+# Relay
+
+Previously the configuration of the Relay integration was focused around the `EnableRelaySupport()` method. It allowed you to enable Global Object Identification and automatically adding a query field to mutation payloads.
+
+The problem is that `EnableRelaySupport()` always enabled the Global Object Identification feature. This is not obviously implied by the name and also prevents you from using the other feature in isolation.
+
+Therefore we introduced two separate APIs to give you more explicit control over which parts of the Relay integration you want to enable.
+
+## Global Object Identification
+
+**v11**
+
+```csharp
+services
+ .AddGraphQLServer()
+ .EnableRelaySupport();
+```
+
+**v12**
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddGlobalObjectIdentification();
+```
+
+[Learn more about Global Object Identification](/docs/hotchocolate/v12/defining-a-schema/relay#global-object-identification)
+
+## Query field in Mutation payloads
+
+**v11**
+
+```csharp
+services
+ .AddGraphQLServer()
+ .EnableRelaySupport(new RelayOptions
+ {
+ AddQueryFieldToMutationPayloads = true,
+ QueryFieldName = "rootQuery",
+ MutationPayloadPredicate = type => type.Name.Value.EndsWith("Result")
+ });
+```
+
+**v12**
+
+```csharp
+sevices
+ .AddGraphQL()
+ .AddQueryFieldToMutationPayloads(options =>
+ {
+ options.QueryFieldName = "rootQuery";
+ options.MutationPayloadPredicate =
+ type => type.Name.Value.EndsWith("Result");
+ });
+```
+
+If you just want to enable the feature without further configuration, you can omit the `options =>` action.
+
+> ⚠️ Note: Since `EnableRelaySupport()` previously always implied the usage of Global Object Identification, you might have to enable Global Object Identification separately as well.
+
+[Learn more about Query field in Mutation payloads](/docs/hotchocolate/v12/defining-a-schema/relay#query-field-in-mutation-payloads)
+
+# DataLoader
+
+We have consolidated the DataLoader base classes into the GreenDonut package which has no dependency on any HotChocolate packages. This allows for people using DataLoader in their business layer without having to reference GraphQL related packages. In your DataLoader classes the namespace `HotChocolate.Fetching` and `HotChocolate.DataLOader` are no longer needed.
+
+Second, we optimized memory usage of DataLoader and it is now best practice to let the DI inject the DataLoaderOptions into the DataLoader.
+
+**v11**
+
+```csharp
+public class CustomBatchDataLoader : BatchDataLoader
+{
+ public CustomBatchDataLoader(IBatchScheduler batchScheduler)
+ : base(batchScheduler)
+ {
+
+ }
+
+ // code omitted for brevity.
+}
+```
+
+**v12**
+
+```csharp
+public class CustomBatchDataLoader : BatchDataLoader
+{
+ public CustomBatchDataLoader(IBatchScheduler batchScheduler, DataLoaderOptions options)
+ : base(batchScheduler, options)
+ {
+
+ }
+
+ // code omitted for brevity.
+}
+```
+
+Allowing the DI to inject the options will allow the DataLoader to use the new shared pooled cache objects.
+
+# Custom naming conventions
+
+If you're using a custom naming convention and have xml documentation enabled, you'll need to modify the way the naming convention is hooked up
+else your comments will disappear from your schema.
+
+**v11**
+
+```csharp
+public class CustomNamingConventions : DefaultNamingConventions
+{
+ public CustomNamingConventions()
+ : base() { }
+}
+
+services
+ .AddGraphQLServer()
+ .AddConvention(sp => new CustomNamingConventions()) // or
+ .AddConvention();
+```
+
+**v12**
+
+```csharp
+public class CustomNamingConventions : DefaultNamingConventions
+{
+ public CustomNamingConventions(IDocumentationProvider documentationProvider)
+ : base(documentationProvider) { }
+}
+
+IReadOnlySchemaOptions capturedSchemaOptions;
+services
+ .AddGraphQLServer()
+ .ModifyOptions(opt => capturedSchemaOptions = opt)
+ .AddConvention(sp => new CustomNamingConventions(
+ new XmlDocumentationProvider(
+ new XmlDocumentationFileResolver(
+ capturedSchemaOptions.ResolveXmlDocumentationFileName),
+ sp.GetApplicationService>()
+ ?? new NoOpStringBuilderPool())));
+```
+
+# Miscellaneous
+
+* `IObjectField`
+ * If you were using `IObjectField.Member`, you'll likely want to move to `IObjectField.ResolverMember` (as `.Member` can be `null` in some cases now where it previously wasn't; and `.ResolverMember` will fall back to `.Member`).
diff --git a/website/src/docs/hotchocolate/v12/api-reference/options.md b/website/src/docs/hotchocolate/v12/api-reference/options.md
new file mode 100644
index 00000000000..d2e45a4a348
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/options.md
@@ -0,0 +1,24 @@
+---
+title: Schema Options
+---
+
+Hot Chocolate distinguishes between schema and execution options. Schema options relate to the type system and execution options to the query engine.
+
+| Member | Type | Default | Description |
+| ---------------------- | -------- | -------------- | --------------------------------------------------------------------------- |
+| `QueryTypeName` | `string` | `Query` | The name of the query type. |
+| `MutationTypeName` | `string` | `Mutation` | The name of the mutation type. |
+| `SubscriptionTypeName` | `string` | `Subscription` | The name of the subscription type. |
+| `StrictValidation` | `bool` | `true` | Defines if the schema is allowed to have errors like missing resolvers etc. |
+
+The schema options allow to alter the overall execution behaviour. The options can be set during schema creation.
+
+```csharp
+SchemaBuilder.New()
+ .ModifyOptions(opt =>
+ {
+ opt.QueryTypeName = "Foo";
+ })
+ ...
+ .Create()
+```
diff --git a/website/src/docs/hotchocolate/v12/api-reference/visitors.md b/website/src/docs/hotchocolate/v12/api-reference/visitors.md
new file mode 100644
index 00000000000..3b04f1f7ec9
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/api-reference/visitors.md
@@ -0,0 +1,149 @@
+---
+title: "Visitors"
+---
+
+Hot Chocolate creates an abstract syntax tree for every incoming request. The execution engine evaluates this syntax tree in many different ways. Validation is a good example. Every incoming request has to be validated. The execution engine has to be sure that the semantic of the requested document is correct. A set of rules is applied to the syntax tree, to find potential semantic flaws.
+
+Usually, you do not have to access the _AST_ directly. The AST only becomes significant, when you want to change execution behavior based on the structure of the query. For example features like _Filtering_, _Sorting_, or _Selection_, analyze the incoming query and generate expressions based on it.
+
+Hot Chocolate provides you with different APIs that support you to traverse these trees. The `SyntaxWalker` is a visitor that has built-in all the logic to _walk down a syntax tree_.
+
+The `SyntaxWalker` is completely stateless. All the state is on a context object that is passed along. The generic argument `TContext` of `SyntaxWalker` denotes the type of the context.
+
+To start the visitation of a _GraphQL_ syntax tree, you have to pass the node and the context the visitation should start from to the visitors `Visit` method.
+
+---
+
+# Visitation
+
+To start the visitation of a _GraphQL_ syntax tree, you have to pass the node and the context the visitation should start from to the visitors `Visit` method. On its way down the syntax tree, the visitor _enters_ a node. The visitor then gets the children of the current node and _enters_ its children. Once the visitor reached a leaf node, it starts walking back up the tree and _leaves_ all the nodes. The visitor provides a virtual `Enter` and a virtual `Leave` method for all _GraphQL_ AST nodes. These methods are called from the visitor as it _enters_ or _leaves_ a node.
+
+The syntax walker provides a few methods in addition to the `Enter` and `Leave` methods. For these two methods, there are also convenience methods that are called right _before_ and _after_ the method call. Namely, `OnBeforeEnter`, `OnAfterEnter`, `OnBeforeLeave`, `OnAfterLeave`.
+These methods can modify the current `TContext`. These _before_ and _after_ methods are good places to initialize state that is used in the main _enter_ or _leave_ method. e.g. before entering a `FieldNode`, you may want to peek the latest type from the context and get the instance of the `ObjectField` corresponding to `FieldNode` of this type. You may also want to push this type onto the context to then use it in the `Enter` method.
+
+> **⚠️ NOTE:** In the following sequence diagram the participants do **NOT** represent any object instances. Furthermore, many steps are hidden in this example. The visualization below should just give you provide you visual insight on the order of the methods being called.
+
+```graphql
+query GetFoos {
+ foo {
+ bar
+ }
+}
+```
+
+```mermaid
+sequenceDiagram
+autonumber
+ Root->>Root: OnBeforeEnter `query GetFoos`
+ Root->>Root: Enter `query GetFoos`
+ Root->>Root: OnAfterEnter `query GetFoos`
+ Root->>Foo: VisitChildren
+ Foo->>Foo: OnBeforeEnter foo
+ Foo->>Foo: Enter foo
+ Foo->>Foo: OnAfterEnter foo
+ Foo->>Bar: VisitChildren
+ Note right of Bar: ...
+ Bar-->Foo: -
+ Foo->>Foo: OnBeforeLeave foo
+ Foo->>Foo: Leave foo
+ Foo->>Foo: OnAfterLeave foo
+ Foo-->Root: -
+ Root->>Root: OnBeforeLeave `query GetFoos`
+ Root->>Root: Leave `query GetFoos`
+ Root->>Root: OnAfterLeave `query GetFoos`
+```
+
+1. We start walking down the tree and _enter_. Call the `csharp±OnBeforeEnter(OperationDefinitionNode node, TContext context)`
+2. Call the `csharp±Enter(OperationDefinitionNode node, TContext context)`
+3. Call the `csharp±OnAfterEnter(OperationDefinitionNode node, TContext context)`
+4. Call the `csharp±VisitChildren(OperationDefinitionNode node, TContext context)`
+5. Call the `csharp±OnBeforeEnter(ObjectFieldNode node, TContext context)`
+6. Call the `csharp±Enter(ObjectFieldNode node, TContext context)`
+7. Call the `csharp±OnAfterEnter(ObjectFieldNode node, TContext context)`
+8. Call the `csharp±VisitChildren(ObjectFieldNode node, TContext context)`
+9. We walk back up the tree and _leave_
+10. Call the `csharp±OnBeforeLeave(ObjectFieldNode node, TContext context)`
+11. Call the `csharp±Leave(ObjectFieldNode node, TContext context)`
+12. Call the `csharp±OnAfterLeave(ObjectFieldNode node, TContext context)`
+13. We walk back up the tree and _leave_.
+14. Call the `csharp±OnBeforeLeave(OperationDefinitionNode node, TContext context)`
+15. Call the `csharp±Leave(OperationDefinitionNode node, TContext context)`
+16. Call the `csharp±OnAfterLeave(OperationDefinitionNode node, TContext context)`
+
+---
+
+# Visitor Actions
+
+The _Enter_ and _Leave_ methods return visitor actions. These methods control the visitors' next step in the visitation. Visitor actions can be used to _skip_ further visitation and step back up, or to _continue_ and walk the current branch of the tree further down.
+
+## Continue
+
+If `Continue` is returned from the `Enter` or `Leave` method visitation on the current branch continues.
+
+In the following example `Continue` is returned from the onEnter method. The visitor calls `VisitChildren` and continues by _entering_ the selection set.
+
+```graphql {4}
+query {
+ foo {
+ bar
+ baz @onEnter(return: CONTINUE) {
+ quux
+ }
+ qux
+ }
+}
+```
+
+## Skip
+
+If `Skip` is returned from the `Enter` or `Leave` method, further visitation on this node stops.
+
+In the following example `Skip` is returned from the onEnter method. The visitor skips the field _baz_. It continues visitation by _entering_ the field _qux_.
+
+```graphql {4}
+query {
+ foo {
+ bar
+ baz @onEnter(return: SKIP) {
+ quux
+ }
+ qux
+ }
+}
+```
+
+## SkipAndLeave
+
+If `SkipAndLeave` is returned from the `Enter` method, further visitation on this node stops. Instead of directly calling the next `Enter` method. The visitor calls the `Leave` method of the current node first.
+
+In the following example `SkipAndLeave` is returned from the onEnter method. The visitor skips the field _baz_. Before it continues visitation with the field _qux_ it _leaves_ the field _baz_ by calling `Leave`
+
+```graphql {4}
+query {
+ foo {
+ bar
+ baz @onEnter(return: SKIPANDLEAVE) {
+ quux
+ }
+ qux
+ }
+}
+```
+
+## Break
+
+If `Break` is returned from the `Enter` or `Leave` method, further visitation on this branch stops.
+
+In the following example `Break` is returned from the onEnter method. The visitor immediately starts walking back up. The visitor calls the `Leave` on `foo` instead of visiting the selections set of _baz_ it skips _baz_ and _qux_.
+
+```graphql {4}
+query {
+ foo {
+ bar
+ baz @onEnter(return: BREAK) {
+ quux
+ }
+ qux
+ }
+}
+```
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/arguments.md b/website/src/docs/hotchocolate/v12/defining-a-schema/arguments.md
new file mode 100644
index 00000000000..256cc15a6cc
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/arguments.md
@@ -0,0 +1,124 @@
+---
+title: "Arguments"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+GraphQL allows us to specify arguments on a field and access their values in the field's resolver.
+
+```sdl
+type Query {
+ user(id: ID!): User
+}
+```
+
+Clients can specify arguments like the following.
+
+```graphql
+{
+ user(id: "123") {
+ username
+ }
+}
+```
+
+Often times arguments will be specified using variables.
+
+```graphql
+query ($userId: ID!) {
+ user(id: $userId) {
+ username
+ }
+}
+```
+
+Learn more about arguments [here](https://graphql.org/learn/schema/#arguments) and variables [here](https://graphql.org/learn/queries/#variables).
+
+# Usage
+
+Arguments can be defined like the following.
+
+
+
+
+```csharp
+public class Query
+{
+ public User GetUser(string username)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+We can also change the name of the argument used in the schema.
+
+```csharp
+public class Query
+{
+ public User GetUser([GraphQLName("name")] string username)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("user")
+ .Argument("username", a => a.Type>())
+ .Resolve(context =>
+ {
+ var username = context.ArgumentValue("username");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+We can also access nullable values through an `Optional`.
+
+```csharp
+var username = context.ArgumentOptional("username");
+
+if (username.HasValue)
+{
+ // use username.Value
+}
+```
+
+
+
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ user(username: String!): User
+ }
+ ")
+ .AddResolver("Query", "user", (context) =>
+ {
+ var username = context.ArgumentValue("username");
+
+ // Omitted code for brevity
+ });
+```
+
+
+
+
+Arguments can be made required by using the non-null type. Learn more about [non-null](/docs/hotchocolate/v12/defining-a-schema/non-null)
+
+If we need to provide complex objects as arguments, we can use [input object types](/docs/hotchocolate/v12/defining-a-schema/input-object-types).
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/directives.md b/website/src/docs/hotchocolate/v12/defining-a-schema/directives.md
new file mode 100644
index 00000000000..71c78a4a588
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/directives.md
@@ -0,0 +1,410 @@
+---
+title: "Directives"
+---
+
+Directives provide a way to add metadata for client tools such as code generators and IDEs or alternate a GraphQL server's runtime execution and type validation behavior.
+
+There are two kinds of directives, executable directives to annotate executable parts of GraphQL documents and type-system directives to annotate SDL types.
+
+Typically, any GraphQL server implementation should provide the following directives `@skip`, `@include`, and `@deprecated`. `@skip` and `@include`, for example, are executable directives used in GraphQL documents to exclude or include fields, whereas `@deprecated` is a type-system directive used in SDL types to inform client tools that a particular part such as a field is deprecated.
+
+# Structure
+
+Directives consist of a name and zero or more arguments. `@skip`, for example, has the name **skip** and a mandatory argument named **if**. Also, `@skip` carries a piece of hidden information only examinable in SDL, namely the location, which specifies where a directive is applicable. Let's take a look at the SDL of the `@skip` directive.
+
+```sdl
+directive @skip(if: Boolean!) on
+ | FIELD
+ | FRAGMENT_SPREAD
+ | INLINE_FRAGMENT
+```
+
+The `directive` keyword in SDL indicates that we're dealing with a directive type declaration. The `@` sign also indicates that this is a directive but more from a usage perspective.
+
+The word `skip` represents the directive's name followed by a pair of parentheses that includes a list of arguments, consisting, in our case, of one argument named `if` of type non-nullable boolean (meaning it is required).
+
+The `on` keyword indicates the location where or at which part a directive is applicable, followed by a list of exact locations separated by pipes `|`. In the case of `@skip`, we can see that we're dealing with an executable directive because this directive is only applicable to fields, fragment-spreads, and inline-fragments.
+
+# Usage
+
+Let's say we have a GraphQL document and want to exclude details under certain circumstances; it would probably look something like this.
+
+```graphql
+query me($excludeDetails: Boolean!) {
+ me {
+ id
+ name
+ ...Details @skip(if: $excludeDetails)
+ }
+}
+
+fragment Details on User {
+ mobileNumber
+ phoneNumber
+}
+```
+
+With `@skip`, we've successfully altered the GraphQL's runtime execution behavior. If `$excludeDetails` is set to `true`, the execution engine will exclude the fields `mobileNumber` and `phoneNumber`; the response would look like this.
+
+```json
+{
+ "data": {
+ "me": {
+ "id": "VXNlcgox",
+ "name": "Henry"
+ }
+ }
+}
+```
+
+Now that we know how to use directives in GraphQL, let's head over to the next section, which is about one crucial aspect of directives.
+
+## Order Matters
+
+**The order of directives is significant**, because the execution is in **sequential order**, which means one after the other. If we have something like the following example, we can see how directives can affect each other.
+
+```graphql
+query me {
+ me {
+ name @skip(if: true) @include(if: true)
+ }
+}
+```
+
+Since we excluded the field `name` in the first place, `@include` does not affect the field `name` anymore. We then just get an empty `me` object in return.
+
+```json
+{
+ "data": {
+ "me": {}
+ }
+}
+```
+
+> **Note:** We will have a deep dive on directives' order under the [Middleware](#order) section.
+
+Now that we have a basic understanding of what directives are, how they work, and what we can do with them, let's create a custom directive.
+
+# Custom Directives
+
+To create a directive, we need to create a new class that inherits from `DirectiveType` and also to override the `Configure` method.
+
+```csharp
+public class MyDirectiveType : DirectiveType
+{
+ protected override void Configure(IDirectiveTypeDescriptor descriptor)
+ {
+ descriptor.Name("my");
+ descriptor.Location(DirectiveLocation.Field);
+ }
+}
+```
+
+[Learn more about Locations](#locations)
+
+We also have to register the directive explicitly.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddDirectiveType();
+```
+
+Let's recap! We have registered a new directive named `my` without any arguments and limited the usage to fields only. A GraphQL query request with our new directive could look like this.
+
+```graphql
+query foo {
+ bar @my
+}
+```
+
+As of now, our custom directive provides no functionality. We will handle that part in the [Middleware](#middleware) section. But before that, let's talk about repeatable directives and arguments.
+
+## Repeatable
+
+By default, directives are not repeatable, which means directives are unique and can only be applied once at a specific location. For example, if we use the `my` directive twice at the field `bar`, we will encounter a validation error. So the following GraphQL query request results in an error if the directive is not repeatable.
+
+```graphql
+query foo {
+ bar @my @my
+}
+```
+
+We can enable repeatability like the following.
+
+```csharp
+public class MyDirectiveType : DirectiveType
+{
+ protected override void Configure(IDirectiveTypeDescriptor descriptor)
+ {
+ descriptor.Name("my");
+ descriptor.Location(DirectiveLocation.Field);
+ descriptor.Repeatable();
+ }
+}
+```
+
+This configuration will translate into the following SDL.
+
+```sdl
+directive @my repeatable on FIELD
+```
+
+## Arguments
+
+A directive can provide additional information through arguments.
+They might also come in handy, in combination with repeatable directives, for reusability purposes.
+
+We can add an argument like the following.
+
+```csharp
+public class MyDirective
+{
+ public string Name { get; set; }
+}
+
+public class MyDirectiveType : DirectiveType
+{
+ protected override void Configure(
+ IDirectiveTypeDescriptor descriptor)
+ {
+ descriptor.Name("my");
+ descriptor.Location(DirectiveLocation.FieldDefinition);
+
+ // The 'Name' property is included as an argument implicitly
+
+ // descriptor
+ // .Argument(f => f.ChangeMe)
+ // .Type>()
+ // .Name("differentName");
+ // descriptor.Ignore(f => f.IgnoreMe);
+ }
+}
+```
+
+If we prefer to not use a backing POCO (``) we an also use the `Argument()` method on the `descriptor`.
+
+```csharp
+public class MyDirectiveType : DirectiveType
+{
+ protected override void Configure(IDirectiveTypeDescriptor descriptor)
+ {
+ descriptor.Name("my");
+ descriptor.Location(DirectiveLocation.Field);
+
+ descriptor
+ .Argument("name")
+ .Type>();
+ }
+}
+```
+
+This configuration will translate into the following SDL.
+
+```sdl
+directive @my(name: String!) on FIELD
+```
+
+## Usage within Types
+
+We could associate the `MyDirectiveType` with an object type like the following.
+
+```csharp
+public class FooType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("Foo");
+ descriptor.Directive("my", new ArgumentNode("name", "bar"));
+ }
+}
+```
+
+> Note: For this to work the `MyDirectiveType` directive needs to have the appropriate location within the schema. In this example it would be `DirectiveLocation.Object`.
+
+Referencing directives using their name is not type-safe and could lead to runtime errors, which are avoidable by using our generic variant of the directive type.
+
+Once we have defined our directive using `DirectiveType`, we can pass an instance of the backing POCO (``) instead of the name of the directive and an `ArgumentNode`.
+
+```csharp
+public class FooType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("Foo");
+ descriptor.Directive(new MyDirective { Name = "bar" });
+ }
+}
+```
+
+Since the directive instance that we have added to our type is now a strong .NET type, we don't have to fear changes to the directive structure or name anymore.
+
+## Locations
+
+A directive can define one or multiple locations, where it can be applied. Multiple locations are seperated by a pipe `|`.
+
+```csharp
+descriptor.Location(DirectiveLocation.Field | DirectiveLocation.Object);
+```
+
+Generally we distinguish between two types of locations: Type system and executable locations.
+
+### Type System Locations
+
+Type system locations specify where we can place a specific directive in the schema. The arguments of directives specified in these locations are fixed. We can query such directives through introspection.
+
+The following schema shows where type system directives can be applied.
+
+```sdl
+directive @schema on SCHEMA
+directive @object on OBJECT
+directive @argumentDefinition on ARGUMENT_DEFINITION
+directive @fieldDefinition on FIELD_DEFINITION
+directive @inputObject on INPUT_OBJECT
+directive @inputFieldDefinition on INPUT_FIELD_DEFINITION
+directive @interface on INTERFACE
+directive @enum on ENUM
+directive @enumValue on ENUM_VALUE
+directive @union on UNION
+directive @scalar on SCALAR
+schema @schema {
+ query: Query
+}
+type Query @object {
+ search(by: SearchInput! @argumentDefinition): SearchResult @fieldDefinition
+}
+input SearchInput @inputObject {
+ searchTerm: String @inputFieldDefinition
+}
+interface HasDescription @interface {
+ description: String
+}
+type Product implements HasDescription {
+ added: DateTime
+ description: String
+}
+enum UserKind @enum {
+ Administrator @enumValue
+ Moderator
+}
+type User {
+ name: String
+ userKind: UserKind
+}
+union SearchResult @union = Product | User
+scalar DateTime @scalar
+```
+
+### Executable Locations
+
+Executable locations specify where a client can place a specific directive, when executing an operation.
+
+Our server defines the following directives.
+
+```sdl
+directive @query on QUERY
+directive @field on FIELD
+directive @fragmentSpread on FRAGMENT_SPREAD
+directive @inlineFragment on INLINE_FRAGMENT
+directive @fragmentDefinition on FRAGMENT_DEFINITION
+directive @mutation on MUTATION
+directive @subscription on SUBSCRIPTION
+```
+
+The following request document shows where we, as a client, can apply these directives.
+
+```graphql
+query getUsers @query {
+ search(by: { searchTerm: "Foo" }) @field {
+ ...DescriptionFragment @fragmentSpread
+ ... on User @inlineFragment {
+ userKind
+ }
+ }
+}
+
+fragment DescriptionFragment on HasDescription @fragmentDefinition {
+ description
+}
+
+mutation createNewUser @mutation {
+ createUser(input: { name: "Ada Lovelace" }) {
+ user {
+ name
+ }
+ }
+}
+
+subscription subscribeToUser @subscription {
+ onUserChanged(id: 1) {
+ user {
+ name
+ }
+ }
+}
+```
+
+## Middleware
+
+What makes directives in Hot Chocolate very useful is the ability to associate a middleware with it. A middleware can alternate the result, or even produce the result, of a field. A directive middleware is only added to a field middleware pipeline when the directive was annotated to the object definition, the field definition or the field.
+
+Moreover, if the directive is repeatable the middleware will be added multiple times to the middleware allowing to build a real pipeline with it.
+
+In order to add a middleware to a directive we could declare it with the descriptor as a delegate.
+
+```csharp
+public class MyDirectiveType : DirectiveType
+{
+ protected override void Configure(
+ IDirectiveTypeDescriptor descriptor)
+ {
+ descriptor.Name("my");
+ descriptor.Location(DirectiveLocation.Object);
+
+ descriptor.Use(next => context =>
+ {
+ context.Result = "Bar";
+ return next.Invoke(context);
+ });
+ }
+}
+```
+
+Directives with middleware or executable directives can be put on object types and on their field definitions or on the field selection in a query. Executable directives on an object type will replace the field resolver of every field of the annotated object type.
+
+### Order
+
+In GraphQL the order of directives is significant and with our middleware we use this order to create a resolver pipeline through which the result flows.
+
+The resolver pipeline consists of a sequence of directive delegates, called one after the other.
+
+Each delegate can perform operations before and after the next delegate. A delegate can also decide to not pass a resolver request to the next delegate, which is called short-circuiting the resolver pipeline.
+Short-circuiting is often desirable because it avoids unnecessary work.
+
+The order of the middleware pipeline is defined by the order of the directives. Since executable directives will flow from the object type to its field definitions, the directives of the type would be called first in the order that they were annotated.
+
+```sdl
+type Query {
+ foo: Bar
+}
+
+type Bar @a @b {
+ baz: String @c @d
+}
+```
+
+So, the directives in the above example would be called in the following order `a, b, c, d`.
+
+If there were more directives in the query, they would be appended to the directives from the type.
+
+```graphql
+{
+ foo {
+ baz @e @f
+ }
+}
+```
+
+So, now the order would be like the following: `a, b, c, d, e, f`.
+
+Every middleware can execute the original resolver function by calling `ResolveAsync()` on the `IDirectiveContext`.
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/documentation.md b/website/src/docs/hotchocolate/v12/defining-a-schema/documentation.md
new file mode 100644
index 00000000000..fdb2ba7179a
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/documentation.md
@@ -0,0 +1,248 @@
+---
+title: Documentation
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+Documentation allows us to enrich our schema with additional information that is useful for a consumer of our API.
+
+In GraphQL we can do this by providing descriptions to our types, fields, etc.
+
+```sdl
+type Query {
+ "A query field"
+ user("An argument" username: String): User
+}
+
+"An object type"
+type User {
+ "A field"
+ username: String
+}
+
+"An enum"
+enum UserRole {
+ "An enum value"
+ ADMINISTRATOR
+}
+```
+
+# Usage
+
+We can define descriptions like the following.
+
+
+
+
+```csharp
+[GraphQLDescription("An object type")]
+public class User
+{
+ [GraphQLDescription("A field")]
+ public string Username { get; set; }
+}
+
+[GraphQLDescription("An enum")]
+public enum UserRole
+{
+ [GraphQLDescription("An enum value")]
+ Administrator
+}
+
+public class Query
+{
+ [GraphQLDescription("A query field")]
+ public User GetUser(
+ [GraphQLDescription("An argument")] string username)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+If the description provided to the `GraphQLDescriptionAttribute` is `null` or made up of only white space, XML documentation comments are used as a fallback.
+
+Learn more about XML documentation below.
+
+
+
+
+```csharp
+public class UserType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("User");
+ descriptor.Description("An object type");
+
+ descriptor
+ .Field(f => f.Username)
+ .Description("A field");
+ }
+}
+
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ descriptor.Name("UserRole");
+ descriptor.Description("An enum");
+
+ descriptor
+ .Value(UserRole.Administrator)
+ .Description("An enum value");
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("user")
+ .Description("A query field")
+ .Argument("username", a => a.Type()
+ .Description("An argument"))
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+The `Description()` methods take precedence over all other forms of documentation. This is true, even if the provided value is `null` or only white space.
+
+
+
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ """"""
+ A query field
+ """"""
+ user(""An argument"" username: String): User
+ }
+
+ """"""
+ An object type
+ """"""
+ type User {
+ ""A field""
+ username: String
+ }
+
+ """"""
+ An enum
+ """"""
+ enum UserRole {
+ ""An enum value""
+ ADMINISTRATOR
+ }
+ ")
+ // Omitted code for brevity
+```
+
+
+
+
+# XML Documentation
+
+Hot Chocolate provides the ability to automatically generate API documentation from our existing [XML documentation](https://docs.microsoft.com/dotnet/csharp/codedoc).
+
+The following will produce the same schema descriptions we declared above.
+
+```csharp
+///
+/// An object type
+///
+public class User
+{
+ ///
+ /// A field
+ ///
+ public string Username { get; set; }
+}
+
+///
+/// An enum
+///
+public enum UserRole
+{
+ ///
+ /// An enum value
+ ///
+ Administrator
+}
+
+public class Query
+{
+ ///
+ /// A query field
+ ///
+ /// An argument
+ public User GetUser(string username)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+To make the XML documentation available to Hot Chocolate, we have to enable `GenerateDocumentationFile` in our `.csproj` file.
+
+```xml
+
+ true
+ $(NoWarn);1591
+
+```
+
+> Note: The `` element is optional. It prevents the compiler from emitting warnings for missing documentation strings.
+
+If we do not want to include XML documentation in our schema, we can set the `UseXmlDocumentation` property on the schema's `ISchemaOptions`.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .ModifyOptions(opt => opt.UseXmlDocumentation = false);
+```
+
+## With a custom naming convention
+
+If you want to use a custom naming convention and XML documentation, ensure you give the convention an instance of the `XmlDocumentationProvider` as demonstrated below; otherwise the comments won't appear in your schema.
+
+```csharp
+public class CustomNamingConventions : DefaultNamingConventions
+{
+ // Before
+ public CustomNamingConventions()
+ : base() { }
+
+ // After
+ public CustomNamingConventions(IDocumentationProvider documentationProvider)
+ : base(documentationProvider) { }
+}
+
+// Startup
+// Before
+.AddConvention(sp => new CustomNamingConventions());
+
+// After
+IReadOnlySchemaOptions capturedSchemaOptions;
+
+services
+ .AddGraphQLServer()
+ .ModifyOptions(opt => capturedSchemaOptions = opt)
+ .AddConvention(sp => new CustomNamingConventions(
+ new XmlDocumentationProvider(
+ new XmlDocumentationFileResolver(
+ capturedSchemaOptions.ResolveXmlDocumentationFileName),
+ sp.GetApplicationService>()
+ ?? new NoOpStringBuilderPool())));
+```
+
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/enums.md b/website/src/docs/hotchocolate/v12/defining-a-schema/enums.md
new file mode 100644
index 00000000000..c068562d604
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/enums.md
@@ -0,0 +1,377 @@
+---
+title: "Enums"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+An Enum is a special kind of [scalar](/docs/hotchocolate/v12/defining-a-schema/scalars) that is restricted to a particular set of allowed values. It can be used as both an input and an output type.
+
+```sdl
+enum UserRole {
+ GUEST,
+ DEFAULT,
+ ADMINISTRATOR
+}
+
+type Query {
+ role: UserRole
+ usersByRole(role: UserRole): [User]
+}
+```
+
+# Usage
+
+Given is the schema from above.
+
+When querying a field returning an enum type, the enum value will be serialized as a string.
+
+**Request**
+
+```graphql
+{
+ role
+}
+```
+
+**Response**
+
+```json
+{
+ "data": {
+ "role": "STANDARD"
+ }
+}
+```
+
+When using an enum value as an argument, it is represented as a literal and **not** a string.
+
+**Request**
+
+```graphql
+{
+ usersByRole(role: ADMINISTRATOR) {
+ id
+ }
+}
+```
+
+When used as a type for a variable, it is represented as a string in the variables object, since JSON does not offer support for literals.
+
+**Request**
+
+Operation:
+
+```graphql
+query ($role: UserRole) {
+ usersByRole(role: $role) {
+ id
+ }
+}
+```
+
+Variables:
+
+```json
+{
+ "role": "ADMINISTRATOR"
+}
+```
+
+# Definition
+
+We can define enums like the following.
+
+
+
+
+```csharp
+public enum UserRole
+{
+ Guest,
+ Standard,
+ Administrator
+}
+
+public class Query
+{
+ public User[] GetUsers(UserRole role)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public enum UserRole
+{
+ Guest,
+ Standard,
+ Administrator
+}
+
+public class UserRoleType : EnumType
+{
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("users")
+ .Argument("role", a => a.Type())
+ .Resolve(context =>
+ {
+ var role = context.ArgumentValue("role");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+Since there could be multiple enum types inheriting from `EnumType`, but differing in their name and values, it is not certain which of these types should be used when we return a `UserRole` CLR type from one of our resolvers.
+
+**Therefore it's important to note that Code-first enum types are not automatically inferred. They need to be explicitly specified or registered.**
+
+We can either [explicitly specify the type on a per-resolver basis](/docs/hotchocolate/v12/defining-a-schema/object-types#explicit-types) or we can register the type once globally:
+
+```csharp
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddType();
+ }
+}
+```
+
+With this configuration each `UserRole` CLR type we return from our resovlers would be assumed to be a `UserRoleType`.
+
+
+
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ user(role: UserRole): User
+ }
+
+ enum UserRole {
+ GUEST,
+ DEFAULT,
+ ADMINISTRATOR
+ }
+ ")
+ .AddResolver("Query", "user", (context) =>-
+ {
+ var role = context.ArgumentValue("role");
+
+ // Omitted code for brevity
+ })
+```
+
+
+
+
+## Non-enum values
+
+In Code-first we can also bind the enum type to any other .NET type, for example a `string`.
+
+```csharp
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ // we need to specify a name or otherwise we will get a conflict
+ // with the built-in StringType
+ descriptor.Name("UserRole");
+
+ descriptor
+ .Value("Default")
+ .Name("STANDARD");
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("users")
+ .Argument("role", a => a.Type())
+ .Resolve(context =>
+ {
+ var role = context.ArgumentValue("role");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+# Binding behavior
+
+In the Annotation-based approach all enum values are implicitly included on the schema enum type. The same is true for `T` of `EnumType` when using the Code-first approach.
+
+In the Code-first approach we can also enable explicit binding, where we have to opt-in enum values we want to include instead of them being implicitly included.
+
+
+
+We can configure our preferred binding behavior globally like the following.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> ⚠️ Note: This changes the binding behavior for all types, not only enum types.
+
+We can also override it on a per type basis:
+
+```csharp
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ descriptor.BindValues(BindingBehavior.Implicit);
+
+ // We could also use the following methods respectively
+ // descriptor.BindValuesExplicitly();
+ // descriptor.BindValuesImplicitly();
+ }
+}
+```
+
+## Ignoring values
+
+
+
+
+In the Annotation-based approach we can ignore values using the `[GraphQLIgnore]` attribute.
+
+```csharp
+public enum UserRole
+{
+ [GraphQLIgnore]
+ Guest,
+ Standard,
+ Administrator
+}
+```
+
+
+
+
+In the Code-first approach we can ignore values using the `Ignore` method on the `IEnumTypeDescriptor`. This is only necessary, if the binding behavior of the enum type is implicit.
+
+```csharp
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ descriptor.Ignore(UserRole.Guest);
+ }
+}
+```
+
+
+
+
+We do not have to ignore values in the Schema-first approach.
+
+
+
+
+## Including values
+
+In the Code-first approach we can explicitly include values using the `Value` method on the `IEnumTypeDescriptor`. This is only necessary, if the binding behavior of the enum type is explicit.
+
+```csharp
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ descriptor.BindValuesExplicitly();
+
+ descriptor.Value(UserRole.Guest);
+ }
+}
+```
+
+# Naming
+
+Unless specified explicitly, Hot Chocolate automatically infers the names of enums and their values. Per default the name of the enum becomes the name of the enum type. When using `EnumType` in Code-first, the name of `T` is chosen as the name for the enum type.
+
+Enum values are automatically formatted to the UPPER_SNAIL_CASE according to the GraphQL specification:
+
+- `Guest` becomes `GUEST`
+- `HeadOfDepartment` becomes `HEAD_OF_DEPARTMENT`
+
+If we need to we can override these inferred names.
+
+
+
+
+The `[GraphQLName]` attribute allows us to specify an explicit name.
+
+```csharp
+[GraphQLName("Role")]
+public enum UserRole
+{
+ [GraphQLName("VISITOR")]
+ Guest,
+ Standard,
+ Administrator
+}
+```
+
+
+
+
+The `Name` method on the `IEnumTypeDescriptor` / `IEnumValueDescriptor` allows us to specify an explicit name.
+
+```csharp
+public class UserRoleType : EnumType
+{
+ protected override void Configure(IEnumTypeDescriptor descriptor)
+ {
+ descriptor.Name("Role");
+
+ descriptor.Value(UserRole.Guest).Name("VISITOR");
+ }
+}
+```
+
+
+
+
+Simply change the names in the schema.
+
+
+
+
+This would produce the following `Role` schema enum type:
+
+```sdl
+enum Role {
+ VISITOR,
+ STANDARD,
+ ADMINISTRATOR
+}
+```
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/extending-types.md b/website/src/docs/hotchocolate/v12/defining-a-schema/extending-types.md
new file mode 100644
index 00000000000..89e2898d447
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/extending-types.md
@@ -0,0 +1,372 @@
+---
+title: "Extending Types"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+Type extensions allow us to add, remove or replace fields on existing types, without necessarily needing access to these types.
+
+Because of these capabilities, they also allow for better organization of our types. We could for example have classes that encapsulate part of our domain and extend our `Query` type with these functionalities.
+
+Type extensions are especially useful if we want to modify third-party types, such as types that live in a separate assembly and are therefore not directly modifiable by us.
+
+
+
+> ⚠️ Note: Type extensions do not produce the [extend type syntax that GraphQL offers](http://spec.graphql.org/draft/#sec-Object-Extensions), since it would unnecessarily complicate the resulting schema. Instead, Hot Chocolate's type extensions are directly merged with the original type definition to create a single type at runtime.
+
+# Object Types
+
+Consider we have the following entity that we want to extend with functionality.
+
+```csharp
+public class Book
+{
+ public int Id { get; set; }
+
+ public string Title { get; set; }
+
+ public int AuthorId { get; set; }
+}
+```
+
+## Adding fields
+
+We can easily add new fields to our existing `Book` type.
+
+
+
+
+```csharp
+[ExtendObjectType(typeof(Book))]
+public class BookExtensions
+{
+ public IEnumerable GetGenres([Parent] Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+One of the most common use-cases for this would be adding new resolvers to one of our root types.
+
+```csharp
+[ExtendObjectType(typeof(Query))]
+public class QueryBookResolvers
+{
+ public IEnumerable GetBooks()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+```csharp
+public class BookTypeExtensions : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("genres")
+ .Type>()
+ .Resolve(context =>
+ {
+ var parent = context.Parent();
+
+ // Omitted code for brevity
+ });
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+One of the most common use-cases for this would be adding new resolvers to one of our root types.
+
+```csharp
+public class QueryTypeBookResolvers : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("books")
+ .Type>()
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+Simply add a new field to the existing type.
+
+
+
+
+## Removing fields
+
+We can also ignore fields of the type we are extending.
+
+
+
+
+```csharp
+[ExtendObjectType(typeof(Book),
+ IgnoreProperties = new[] { nameof(Book.AuthorId) })]
+public class BookExtensions
+{
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+```csharp
+public class BookTypeExtensions : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Ignore(f => f.AuthorId);
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+Simply remove the field from the existing type.
+
+
+
+
+## Replacing fields
+
+We might have an `Id` field, which we want to replace with a field that resolves the actual type the `Id` is pointing to.
+
+In this example we replace the `authorId` field with an `author` field.
+
+
+
+
+```csharp
+[ExtendObjectType(typeof(Book))]
+public class BookExtensions
+{
+ [BindMember(nameof(Book.AuthorId))]
+ public Author GetAuthor([Parent] Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+**This is currently not working ([#3776](https://github.com/ChilliCream/hotchocolate/issues/3776))**
+
+```csharp
+public class BookTypeExtensions : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.AuthorId)
+ .Type()
+ .Name("author")
+ .Resolve(context =>
+ {
+ var parent = context.Parent();
+
+ // Omitted code for brevity
+ });
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+ }
+}
+```
+
+
+
+
+Simply replace the field on the existing type.
+
+
+
+
+## Extending by name
+
+If we can not reference a type, we can still extend it by specifying its name.
+
+
+
+
+```csharp
+[ExtendObjectType("Foo")]
+public class FooExtensions
+{
+ // Omitted code for brevity
+}
+```
+
+
+
+
+```csharp
+public class FooTypeExtensions : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("Foo");
+
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+⚠️ Schema-first does not currently support extending types by their name
+
+
+
+
+When extending root types, we can make use of the constants in `OperationTypeNames`. We can for example use `OperationTypeNames.Query` instead of writing `"Query"` everywhere.
+
+## Extending base types
+
+We can also extend multiple types at once, but still dedicate specific resolvers to specific types.
+
+```csharp
+// this extends every type that inherits from object (essentially every type)
+[ExtendObjectType(typeof(object))]
+public class ObjectExtensions
+{
+ // this field is added to every object type
+ public string NewField()
+ {
+ // Omitted code for brevity
+ }
+
+ // this field is only added to the Book type
+ public Author GetAuthor([Parent] Book book)
+ {
+ // Omitted code for brevity
+ }
+
+ // this field is only added to the Author type
+ public IEnumerable GetBooks([Parent] Author author)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+We can also modify all object types that are connected by a base type, like an interface.
+
+```csharp
+[InterfaceType]
+public interface IPost
+{
+ string Title { get; set; }
+}
+
+// this extends every type that implements the IPost interface,
+// not the interface type itself
+[ExtendObjectType(typeof(IPost))]
+public class PostExtensions
+{
+ public string NewField([Parent] IPost post)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+> Note: The `IPost` is annotated with `[InterfaceType]` to include it in the GraphQL schema, but that isn't necessary for the type extension to work.
+> We can use any base type, like `object` or an `abstract` base class, as an extension point without necessarily exposing the base type in our GraphQL schema.
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/index.md b/website/src/docs/hotchocolate/v12/defining-a-schema/index.md
new file mode 100644
index 00000000000..a5a14540ef5
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/index.md
@@ -0,0 +1,89 @@
+---
+title: "Overview"
+---
+
+In this section we will learn everything that is needed to build an expressive GraphQL schema.
+
+# Operations
+
+First we will look at the three root types, often called _Operations_, that represent entry points to our schema:
+
+- Queries allow us to _query_ our graph and retrieve data in a readonly manner. [Learn more about queries](/docs/hotchocolate/v12/defining-a-schema/queries)
+
+- Mutations allow us to _mutate_ our graph entities in the form of adding, removing or updating entities. [Learn more about mutations](/docs/hotchocolate/v12/defining-a-schema/mutations)
+
+- Subscriptions allow us to _subscribe_ to events in our system and be notified in real-time of their occurrence. [Learn more about subscriptions](/docs/hotchocolate/v12/defining-a-schema/subscriptions)
+
+# Types
+
+Each GraphQL schema is made up of two basic building blocks:
+
+- Object types contain fields and describe our entities. [Learn more about object types](/docs/hotchocolate/v12/defining-a-schema/object-types)
+
+- Scalars are the primitives of our GraphQL schema: `String`, `Int`, etc. We can also define custom scalars to more precisely describe our business domain. [Learn more about scalars](/docs/hotchocolate/v12/defining-a-schema/scalars)
+
+There are also more advanced types:
+
+- Enums are a special kind of scalar, restricted to a particular set of allowed values. [Learn more about enums](/docs/hotchocolate/v12/defining-a-schema/enums)
+- Interfaces represent a shared contract that other types can implement. [Learn more about interfaces](/docs/hotchocolate/v12/defining-a-schema/interfaces)
+- Unions represent a set of object types, without the need for a shared contract. [Learn more about unions](/docs/hotchocolate/v12/defining-a-schema/unions).
+
+# Type Modifiers
+
+Besides regular types, like scalars and object types, there are also _type modifiers_.
+
+A non-null field for example indicates that a client can always expect a non-null value to be returned from the field.
+
+[Learn more about non-null](/docs/hotchocolate/v12/defining-a-schema/non-null)
+
+List fields indicate to a client that the field will return a list in the specified shape.
+
+[Learn more about lists](/docs/hotchocolate/v12/defining-a-schema/lists)
+
+# Arguments
+
+We can pass arguments to individual fields on an object type and access their values inside the field's resolver.
+
+[Learn more about arguments](/docs/hotchocolate/v12/defining-a-schema/arguments)
+
+Nested object types can also be used as arguments by declaring so called input object types. These are most commonly used when passing a payload to a mutation.
+
+[Learn more about input object types](/docs/hotchocolate/v12/defining-a-schema/input-object-types)
+
+# Extending Types
+
+Hot Chocolate allows us to extend existing types, helping us keep our code organized.
+
+Rather than adding more and more fields to the Query type in the same class for instance, we can _extend_ the Query type with a new field from another location in our codebase where that field logically should live.
+
+[Learn more about extending types](/docs/hotchocolate/v12/defining-a-schema/extending-types)
+
+# Directives
+
+Directives allow us to decorate parts of our GraphQL schema with additional configuration.
+
+This configuration can be used as metadata for client tools or alternate our GraphQL server's runtime execution and type validation behavior.
+
+[Learn more about directives](/docs/hotchocolate/v12/defining-a-schema/directives)
+
+# Schema evolution
+
+As our data graph and number of developers/clients grows, we need to ensure that the graph is understood by everyone. Therefore, our schema should expose as much information to consumers of our API as possible.
+
+[Learn more about schema documentation](/docs/hotchocolate/v12/defining-a-schema/documentation)
+
+[Learn more about versioning](/docs/hotchocolate/v12/defining-a-schema/versioning)
+
+# Relay
+
+[Relay](https://relay.dev) proposes some schema design principles for GraphQL servers in order to more efficiently fetch, refetch and cache entities on the client. Since these principles make for a better schema, we encourage all users, not only those of Relay, to consider these principles.
+
+[Learn more about Relay-compatible schema design](/docs/hotchocolate/v12/defining-a-schema/relay)
+
+# Automatic type registration
+
+Starting with Hot Chocolate 12.7 we introduced a new source generator that automatically registers types and DataLoader with your GraphQL configuration builder. Watch on YouTube how you can simplify your Hot Chocolate configuration code.
+
+
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/input-object-types.md b/website/src/docs/hotchocolate/v12/defining-a-schema/input-object-types.md
new file mode 100644
index 00000000000..a3f8c98ba99
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/input-object-types.md
@@ -0,0 +1,506 @@
+---
+title: "Input Object Types"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+We already looked at [arguments](/docs/hotchocolate/v12/defining-a-schema/arguments), which allow us to use simple [scalars](/docs/hotchocolate/v12/defining-a-schema/scalars) like `String` to pass data into a field. GraphQL defines input object types to allow us to use objects as arguments on our fields.
+
+Input object type definitions differ from [object types](/docs/hotchocolate/v12/defining-a-schema/object-types) only in the used keyword and in that their fields can not have arguments.
+
+```sdl
+input BookInput {
+ title: String
+ author: String
+}
+```
+
+# Usage
+
+Input object types can be defined like the following.
+
+
+
+
+```csharp
+public class BookInput
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+public class Mutation
+{
+ public async Task AddBook(BookInput input)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+> Note: If a class is used as an argument to a resolver and it does not end in `Input`, Hot Chocolate (by default) will append `Input` to the type name in the resulting schema.
+
+We can also use a class both as an output- and an input-type.
+
+```csharp
+public class Book
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+public class Mutation
+{
+ public async Task AddBook(Book input)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+This will produce the following schema.
+
+```sdl
+type Book {
+ title: String
+ author: String
+}
+
+input BookInput {
+ title: String
+ author: String
+}
+
+type Mutation {
+ addBook(input: BookInput): Book
+}
+```
+
+> Note: While it is possible, it is not encouraged, as it complicates future extensions of either type.
+
+
+
+
+```csharp
+public class BookInput
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+public class BookInputType : InputObjectType
+{
+ protected override void Configure(
+ IInputObjectTypeDescriptor descriptor)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Mutation);
+
+ descriptor
+ .Field("addBook")
+ .Argument("input", a => a.Type())
+ .Resolve(context =>
+ {
+ var input = context.ArgumentValue("input");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+The `IInputTypeDescriptor` is really similar to the `IObjectTypeDescriptor` and provides almost the same capabilities.
+
+[Learn more about object types](/docs/hotchocolate/v12/defining-a-schema/object-types)
+
+
+
+
+```csharp
+public class BookInput
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ input BookInput {
+ title: String
+ author: String
+ }
+
+ type Mutation {
+ addBook(input: BookInput): Book
+ }
+ ")
+ .BindRuntimeType()
+ .AddResolver( "Mutation", "addBook", (context) =>
+ {
+ var input = context.ArgumentValue("input");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+## Immutable types
+
+If we want our input type classes to be immutable, or we are using [nullable reference types](https://docs.microsoft.com/dotnet/csharp/nullable-references), we can provide a non-empty constructor and Hot Chocolate will instead use that when instantiating the input. Just note that
+
+1. The type of the argument must exactly match the property's type
+2. The name of the argument must match the property name (bar a lowercase first letter)
+3. No setters will be called, so you need to provide arguments for all the properties.
+
+Hot Chocolate validates any custom input constructor at schema build time, so we don't need to worry about breaking things during refactoring!
+
+```csharp
+public class BookInput
+{
+ // No need for the setters now
+ public string Title { get; }
+ public string Author { get; }
+
+ public BookingInput(string title, string author)
+ {
+ Title = title;
+ Author = author;
+ }
+}
+```
+
+We can also use record types, if we're on C# 9.0+. The equivalent to the above would be:
+
+```csharp
+public record BookingInput(string Title, string Author);
+```
+
+## Optional Properties
+
+If we want our input type classes to contain optional properties, we can use the `Optional` type or mark the properties of the class as `nullable`. It is important to also define a default value for any non-nullable property that is using the `Optional` type by adding the `[DefaultValue]` attribute, otherwise the field will still be required when defining the input.
+
+```csharp
+public class BookInput
+{
+ [DefaultValue("")]
+ public Optional Title { get; set; }
+ public string Author { get; set; }
+
+ public BookInput(string title, string author)
+ {
+ Title = title;
+ Author = author;
+ }
+}
+
+```
+
+Also with record types, the equivalent of the above would be:
+
+```csharp
+public record BookInput([property:DefaultValue("")]Optional Title, string Author);
+
+```
+
+## Oneof Input Objects
+
+Oneof Input Objects are a special variant of Input Objects where the type system asserts that exactly one of the fields must be set and non-null, all others being omitted. This is represented in introspection with the \_\_Type.oneField: Boolean field, and in SDL via the @oneOf directive on the input object.
+
+> ⚠️ Note: Oneof Input Objects is currently a draft feature to the GraphQL spec. https://github.com/graphql/graphql-spec/pull/825
+
+
+
+This introduces a form of input polymorphism to GraphQL. For example, the following PetInput input object lets you choose between a number of potential input types:
+
+```sdl
+input PetInput @oneOf {
+ cat: CatInput
+ dog: DogInput
+ fish: FishInput
+}
+
+input CatInput { name: String!, numberOfLives: Int }
+input DogInput { name: String!, wagsTail: Boolean }
+input FishInput { name: String!, bodyLengthInMm: Int }
+
+type Mutation {
+ addPet(pet: PetInput!): Pet
+}
+```
+
+Since the Oneof Input Objects RFC is not yet in the draft stage it is still an opt-in feature. In order to activate it set the schema options to enable it.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ ...
+ .ModifyOptions(o => o.EnableOneOf = true);
+```
+
+Once activate you can create Oneof Input Objects like the following:
+
+
+
+
+```csharp
+[OneOf]
+public class PetInput
+{
+ public Dog? Dog { get; set; }
+
+ public Cat? Cat { get; set; }
+}
+
+public interface IPet
+{
+ string Name { get; }
+}
+
+public class Dog : IPet
+{
+ public string Name { get; set; }
+}
+
+public class Cat : IPet
+{
+ public string Name { get; set; }
+}
+
+public class Mutation
+{
+ public Task CreatePetAsync(PetInput input)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+This will produce the following schema.
+
+```sdl
+input PetInput @oneOf {
+ dog: DogInput
+ cat: CatInput
+}
+
+input DogInput {
+ name: String!
+}
+
+input CatInput {
+ name: String!
+}
+
+interface Pet {
+ name: String!
+}
+
+type Dog implements Pet {
+ name: String!
+}
+
+type Cat implements Pet {
+ name: String!
+}
+
+type Mutation {
+ createPet(input: PetInput): Pet
+}
+```
+
+
+
+
+```csharp
+public class PetInput
+{
+ public Dog? Dog { get; set; }
+
+ public Cat? Cat { get; set; }
+}
+
+public class Dog
+{
+ public string Name { get; set; }
+}
+
+public class Cat
+{
+ public string Name { get; set; }
+}
+
+public class PetType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor
+ .Name("Pet")
+ .Field("name")
+ .Type("String!");
+ }
+}
+
+public class PetInputType : InputObjectType
+{
+ protected override void Configure(
+ IInputObjectTypeDescriptor descriptor)
+ {
+ descriptor.OneOf();
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Mutation);
+
+ descriptor
+ .Field("createPet")
+ .Argument("input", a => a.Type())
+ .Resolve(context =>
+ {
+ var input = context.ArgumentValue("input");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+This will produce the following schema.
+
+```sdl
+input PetInput @oneOf {
+ dog: DogInput
+ cat: CatInput
+}
+
+input DogInput {
+ name: String!
+}
+
+input CatInput {
+ name: String!
+}
+
+interface Pet {
+ name: String!
+}
+
+type Dog implements Pet {
+ name: String!
+}
+
+type Cat implements Pet {
+ name: String!
+}
+
+type Mutation {
+ createPet(input: PetInput): Pet
+}
+```
+
+
+
+
+```csharp
+public class PetInput
+{
+ public Dog? Dog { get; set; }
+
+ public Cat? Cat { get; set; }
+}
+
+public interface IPet
+{
+ string Name { get; }
+}
+
+public class Dog : IPet
+{
+ public string Name { get; set; }
+}
+
+public class Cat : IPet
+{
+ public string Name { get; set; }
+}
+
+public class Mutation
+{
+ public Task CreatePetAsync(PetInput input)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ input PetInput @oneOf {
+ dog: DogInput
+ cat: CatInput
+ }
+
+ input DogInput {
+ name: String!
+ }
+
+ input CatInput {
+ name: String!
+ }
+
+ interface Pet {
+ name: String!
+ }
+
+ type Dog implements Pet {
+ name: String!
+ }
+
+ type Cat implements Pet {
+ name: String!
+ }
+
+ type Mutation {
+ createPet(input: PetInput): Pet
+ }
+ ")
+ .BindRuntimeType()
+ .BindRuntimeType()
+ .BindRuntimeType()
+ .ModifyOptions(o => o.EnableOneOf = true);
+ }
+}
+```
+
+
+
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/interfaces.md b/website/src/docs/hotchocolate/v12/defining-a-schema/interfaces.md
new file mode 100644
index 00000000000..79682978eb5
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/interfaces.md
@@ -0,0 +1,638 @@
+---
+title: "Interfaces"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+An interface is an abstract type that defines a certain set of fields that an object type or another interface must include to implement the interface. Interfaces can only be used as output types, meaning we can't use interfaces as arguments or as fields on input object types.
+
+```sdl
+interface Message {
+ author: User!
+ createdAt: DateTime!
+}
+
+type TextMessage implements Message {
+ author: User!
+ createdAt: DateTime!
+ content: String!
+}
+
+type Query {
+ messages: [Message]!
+}
+```
+
+# Usage
+
+GIven is the schema from above.
+
+When querying a field returning an interface, we can query the fields defined in the interface like we would query a regular object type.
+
+```graphql
+{
+ messages {
+ createdAt
+ }
+}
+```
+
+If we need to access fields that are part of an object type implementing the interface, we can do so using [fragments](https://graphql.org/learn/queries/#fragments).
+
+```graphql
+{
+ messages {
+ createdAt
+ ... on TextMessage {
+ content
+ }
+ }
+}
+```
+
+# Definition
+
+Interfaces can be defined like the following.
+
+
+
+
+```csharp
+[InterfaceType("Message")]
+public interface IMessage
+{
+ User Author { get; set; }
+
+ DateTime CreatedAt { get; set; }
+}
+
+public class TextMessage : IMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class Query
+{
+ public IMessage[] GetMessages()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType();
+ }
+}
+```
+
+We can also use classes to define an interface.
+
+```csharp
+[InterfaceType]
+public abstract class Message
+{
+ public User SendBy { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+}
+
+public class TextMessage : Message
+{
+ public string Content { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ // ...
+ .AddType();
+ }
+}
+```
+
+
+
+
+```csharp
+public interface IMessage
+{
+ User Author { get; set; }
+
+ DateTime CreatedAt { get; set; }
+}
+
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Name("Message");
+ }
+}
+
+public class TextMessage : IMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class TextMessageType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("TextMessage");
+
+ // The interface that is being implemented
+ descriptor.Implements();
+ }
+}
+
+public class Query
+{
+ public IMessage[] GetMessages()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetMessages(default));
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType();
+ }
+}
+```
+
+
+
+
+```csharp
+public interface IMessage
+{
+ User Author { get; set; }
+
+ DateTime CreatedAt { get; set; }
+}
+
+public class TextMessage : IMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ messages: [Message]
+ }
+
+ interface Message {
+ author: User!
+ createdAt: DateTime!
+ }
+
+ type TextMessage implements Message {
+ author: User!
+ createdAt: DateTime!
+ content: String!
+ }
+ ")
+ .BindRuntimeType()
+ .AddResolver("Query", "messages", (context) =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+> Note: We have to explicitly register the interface implementations:
+>
+> ```csharp
+> services.AddGraphQLServer().AddType()
+> ```
+
+# Binding behavior
+
+In the Annotation-based approach all public properties and methods are implicitly mapped to fields on the schema interface type. The same is true for `T` of `InterfaceType` when using the Code-first approach.
+
+In the Code-first approach we can also enable explicit binding, where we have to opt-in properties and methods we want to include instead of them being implicitly included.
+
+
+
+We can configure our preferred binding behavior globally like the following.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> ⚠️ Note: This changes the binding behavior for all types, not only interface types.
+
+We can also override it on a per type basis:
+
+```csharp
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.BindFields(BindingBehavior.Implicit);
+
+ // We could also use the following methods respectively
+ // descriptor.BindFieldsExplicitly();
+ // descriptor.BindFieldsImplicitly();
+ }
+}
+```
+
+## Ignoring fields
+
+
+
+
+In the Annotation-based approach we can ignore fields using the `[GraphQLIgnore]` attribute.
+
+```csharp
+public interface IMessage
+{
+ [GraphQLIgnore]
+ User Author { get; set; }
+
+ DateTime CreatedAt { get; set; }
+}
+```
+
+
+
+
+In the Code-first approach we can ignore fields using the `Ignore` method on the `IInterfaceTypeDescriptor`. This is only necessary, if the binding behavior of the interface type is implicit.
+
+```csharp
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Ignore(f => f.Author);
+ }
+}
+
+```
+
+
+
+
+We do not have to ignore fields in the Schema-first approach.
+
+
+
+
+## Including fields
+
+In the Code-first approach we can explicitly include properties of our POCO using the `Field` method on the `IInterfaceTypeDescriptor`. This is only necessary, if the binding behavior of the interface type is explicit.
+
+```csharp
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+
+ descriptor.Field(f => f.Title);
+ }
+}
+```
+
+# Naming
+
+Unless specified explicitly, Hot Chocolate automatically infers the names of interface types and their fields. Per default the name of the interface / abstract class becomes the name of the interface type. When using `InterfaceType` in Code-first, the name of `T` is chosen as the name for the interface type. The names of methods and properties on the respective interface / abstract class are chosen as names of the fields of the interface type
+
+If we need to we can override these inferred names.
+
+
+
+
+The `[GraphQLName]` attribute allows us to specify an explicit name.
+
+```csharp
+[GraphQLName("Post")]
+public interface IMessage
+{
+ User Author { get; set; }
+
+ [GraphQLName("addedAt")]
+ DateTime CreatedAt { get; set; }
+}
+```
+
+We can also specify a name for the interface type using the `[InterfaceType]` attribute.
+
+```csharp
+[InterfaceType("Post")]
+public interface IMessage
+```
+
+
+
+
+The `Name` method on the `IInterfaceTypeDescriptor` / `IInterfaceFieldDescriptor` allows us to specify an explicit name.
+
+```csharp
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Name("Post");
+
+ descriptor
+ .Field(f => f.CreatedAt)
+ .Name("addedAt");
+ }
+}
+```
+
+
+
+
+Simply change the names in the schema.
+
+
+
+
+This would produce the following `Post` schema interface type:
+
+```sdl
+interface Post {
+ author: User!
+ addedAt: DateTime!
+}
+```
+
+# Interfaces implementing interfaces
+
+Interfaces can also implement other interfaces.
+
+```sdl
+interface Message {
+ author: User
+}
+
+interface DatedMessage implements Message {
+ createdAt: DateTime!
+ author: User
+}
+
+type TextMessage implements DatedMessage & Message {
+ author: User
+ createdAt: DateTime!
+ content: String
+}
+```
+
+We can implement this like the following.
+
+
+
+
+```csharp
+[InterfaceType("Message")]
+public interface IMessage
+{
+ User Author { get; set; }
+}
+
+[InterfaceType("DatedMessage")]
+public interface IDatedMessage : IMessage
+{
+ DateTime CreatedAt { get; set; }
+}
+
+public class TextMessage : IDatedMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class Query
+{
+ public IMessage[] GetMessages()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType()
+ .AddType();
+ }
+}
+```
+
+
+
+
+```csharp
+public interface IMessage
+{
+ User Author { get; set; }
+}
+
+public class MessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Name("Message");
+ }
+}
+
+public interface IDatedMessage : IMessage
+{
+ DateTime CreatedAt { get; set; }
+}
+
+public class DatedMessageType : InterfaceType
+{
+ protected override void Configure(
+ IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Name("DatedMessage");
+
+ descriptor.Implements();
+ }
+}
+
+public class TextMessage : IDatedMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class TextMessageType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("TextMessage");
+
+ // The interface that is being implemented
+ descriptor.Implements();
+ }
+}
+
+public class Query
+{
+ public IMessage[] GetMessages()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetMessages(default));
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType()
+ .AddType();
+ }
+}
+```
+
+
+
+
+```csharp
+public interface IMessage
+{
+ User Author { get; set; }
+}
+
+public interface IDatedMessage : IMessage
+{
+ DateTime CreatedAt { get; set; }
+}
+
+public class TextMessage : IDatedMessage
+{
+ public User Author { get; set; }
+
+ public DateTime CreatedAt { get; set; }
+
+ public string Content { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ messages: [Message]
+ }
+
+ interface Message {
+ author: User
+ }
+
+ interface DatedMessage implements Message {
+ createdAt: DateTime!
+ author: User
+ }
+
+ type TextMessage implements DatedMessage & Message {
+ author: User
+ createdAt: DateTime!
+ content: String
+ }
+ ")
+ .BindRuntimeType()
+ .AddResolver("Query", "messages", (context) =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+> Note: We also have to register the `DatedMessage` interface manually, if we do not expose it through a field directly:
+>
+> ```csharp
+> services.AddGraphQLServer().AddType()
+> ```
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/lists.md b/website/src/docs/hotchocolate/v12/defining-a-schema/lists.md
new file mode 100644
index 00000000000..c6b3be74408
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/lists.md
@@ -0,0 +1,105 @@
+---
+title: "Lists"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+GraphQL allows us to return lists of elements from our fields.
+
+```sdl
+type Query {
+ users: [User]
+}
+```
+
+Clients can query list fields like any other field.
+
+```graphql
+{
+ users {
+ id
+ name
+ }
+}
+```
+
+Querying a list field will result in an ordered list containing elements with the specified subselection of fields.
+
+Learn more about lists [here](https://graphql.org/learn/schema/#lists-and-non-null).
+
+# Usage
+
+Lists can be defined like the following.
+
+
+
+
+If our field resolver returns a list type, e.g. `IEnumerable` or `IQueryable`, it will automatically be treated as a list type in the schema.
+
+```csharp
+public class Query
+{
+ public List GetUsers()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+If our field resolver returns a list type, e.g. `IEnumerable` or `IQueryable`, it will automatically be treated as a list type in the schema.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("users")
+ .Resolve(context =>
+ {
+ List users = null;
+
+ // Omitted code for brevity
+
+ return users;
+ });
+ }
+}
+```
+
+We can also be more explicit by specifying a `ListType` as the return type.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("users")
+ .Type>()
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+```sdl
+type Query {
+ users: [User]
+}
+```
+
+
+
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/mutations.md b/website/src/docs/hotchocolate/v12/defining-a-schema/mutations.md
new file mode 100644
index 00000000000..a6758a8454a
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/mutations.md
@@ -0,0 +1,1054 @@
+---
+title: "Mutations"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+The mutation type in GraphQL is used to mutate/change data. This means that when we are doing mutations, we are intending to cause side-effects in the system.
+
+GraphQL defines mutations as top-level fields on the mutation type. Meaning only the fields on the mutation root type itself are mutations. Everything that is returned from a mutation field represents the changed state of the server.
+
+```sdl
+type Mutation {
+ addBook(input: AddBookInput!): AddBookPayload!
+ publishBook(input: PublishBookInput!): PublishBookPayload!
+}
+```
+
+Clients can execute one or more mutations through the mutation type.
+
+```graphql
+mutation {
+ addBook(input: { title: "C# in depth" }) {
+ book {
+ id
+ title
+ }
+ }
+ publishBook(input: { id: 1 }) {
+ book {
+ publishDate
+ }
+ }
+}
+```
+
+Each of these mutations is executed serially one by one whereas their child selection sets are executed possibly in parallel since only top-level mutation fields (those directly under `mutation`) are allowed to cause side-effects in GraphQL.
+
+# Usage
+
+A mutation type can be defined like the following.
+
+
+
+
+```csharp
+public class Mutation
+{
+ public async Task AddBook(Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddMutationType();
+ }
+
+ // Omitted code for brevity
+}
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public async Task AddBook(Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(f => f.AddBook(default));
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddMutationType();
+ }
+}
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public async Task AddBook(Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Mutation {
+ addBook(input: BookInput): Book
+ }
+
+ input BookInput {
+ title: String
+ author: String
+ }
+
+ type Book {
+ title: String
+ author: String
+ }
+ ")
+ .BindRuntimeType();
+ }
+}
+```
+
+
+
+
+> ⚠️ Note: Only **one** mutation type can be registered using `AddMutationType()`. If we want to split up our mutation type into multiple classes, we can do so using type extensions.
+>
+> [Learn more about extending types](/docs/hotchocolate/v12/defining-a-schema/extending-types)
+
+A mutation type is just a regular object type, so everything that applies to an object type also applies to the mutation type (this is true for all root types).
+
+[Learn more about object types](/docs/hotchocolate/v12/defining-a-schema/object-types)
+
+# Transactions
+
+With multiple mutations executed serially in one request it can be useful to wrap these in a transaction that we can control.
+
+Hot Chocolate provides for this the `ITransactionScopeHandler` which is used by the operation execution middleware to create transaction scopes for mutation requests.
+
+Hot Chocolate provides a default implementation based on the `System.Transactions.TransactionScope` which works with Microsoft ADO.NET data provider and hence can be used in combination with Entity Framework.
+
+The default transaction scope handler can be added like the following.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddDefaultTransactionScopeHandler();
+```
+
+This is how the default implementation looks like:
+
+```csharp
+///
+/// Represents the default mutation transaction scope handler implementation.
+///
+public class DefaultTransactionScopeHandler : ITransactionScopeHandler
+{
+ ///
+ /// Creates a new transaction scope for the current
+ /// request represented by the .
+ ///
+ ///
+ /// The GraphQL request context.
+ ///
+ ///
+ /// Returns a new .
+ ///
+ public virtual ITransactionScope Create(IRequestContext context)
+ {
+ return new DefaultTransactionScope(
+ context,
+ new TransactionScope(
+ TransactionScopeOption.Required,
+ new TransactionOptions
+ {
+ IsolationLevel = IsolationLevel.ReadCommitted
+ }));
+ }
+}
+```
+
+If we implement a custom transaction scope handler or if we choose to extend upon the default transaction scope handler, we can add it like the following.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .AddTransactionScopeHandler();
+```
+
+# Conventions
+
+In GraphQL, it is best practice to have a single argument on mutations called `input`, and each mutation should return a payload object.
+The payload object allows to read the changes of the mutation or to access the domain errors caused by a mutation.
+
+```sdl
+type Mutation {
+ updateUserName(input: UpdateUserNameInput!): UpdateUserNamePayload!
+}
+
+input UpdateUserNameInput {
+ userId: ID!
+ username: String!
+}
+
+type UpdateUserNamePayload {
+ user: User
+}
+```
+
+Following this pattern helps to keep the schema evolvable but requires a lot of boilerplate code to realize.
+
+## Input and Payload
+
+HotChocolate has built-in conventions for mutations to minimize boilerplate code.
+
+The HotChocolate mutation conventions are opt-in and can be enabled like the following:
+
+```csharp
+service
+ .AddGraphQLServer()
+ .AddMutationConventions()
+ ...
+```
+
+With the mutation conventions enabled, we can define the described mutation pattern with minimal code by just annotating a field with `UseMutationConvention`.
+
+
+
+
+```csharp
+public class Mutation
+{
+ [UseMutationConvention]
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public User UpdateUserNameAsync(
+ Guid userId,
+ string username)
+ => ...
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default, default))
+ .Argument("userId", a => a.ID())
+ .UseMutationConvention();
+ }
+}
+```
+
+
+
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!) : User @useMutationConvention
+}
+```
+
+
+
+
+We also can configure the mutation conventions to be applied to all mutations by default.
+
+```csharp
+service
+ .AddGraphQLServer()
+ .AddMutationConventions(applyToAllMutations: true)
+ ...
+```
+
+In the case that the conventions are applied by default we no longer need any annotation.
+
+
+
+
+```csharp
+public class Mutation
+{
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public User UpdateUserNameAsync(
+ Guid userId,
+ string username)
+ => ...
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default, default))
+ .Argument("userId", a => a.ID());
+ }
+}
+```
+
+
+
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!) : User
+}
+```
+
+
+
+
+## Errors
+
+The mutation conventions also allow you to create mutations that follow the error
+[stage 6a Pattern Marc-Andre Giroux layed out](https://xuorig.medium.com/a-guide-to-graphql-errors-bb9ba9f15f85) with minimal effort.
+
+The basic concept here is to keep the resolver clean of any error handling code and use exceptions to signal an error state. The field will simply expose which exceptions are domain errors that shall be exposed to the schema. All other exceptions will still cause runtime errors.
+
+
+
+
+```csharp
+public class Mutation
+{
+ [Error(typeof(UserNameTakenException))]
+ [Error(typeof(InvalidUserNameException))]
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ // ...
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default))
+ .Error()
+ .Error();
+ }
+}
+```
+
+
+
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!): User
+}
+```
+
+```csharp
+public class Mutation
+{
+ [Error(typeof(UserNameTakenException))]
+ [Error(typeof(InvalidUserNameException))]
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+The HotChocolate schema is automatically rewritten, and an error middleware will catch all the exceptions that represent domain errors and rewrite them into the correct error object.
+
+The configuration above emits the following schema:
+
+```sdl
+type Mutation {
+ updateUserName(input: UpdateUserNameInput!): UpdateUserNamePayload!
+}
+
+input UpdateUserNameInput {
+ userId: ID!
+ username: String!
+}
+
+type UpdateUserNamePayload {
+ user: User
+ errors: [UpdateUserNameError!]
+}
+
+type User {
+ username: String
+}
+
+interface Error {
+ message: String!
+}
+
+type UserNameTakenError implements Error {
+ message: String!
+}
+
+type InvalidUserNameError implements Error {
+ message: String!
+}
+
+union UpdateUserNameError = UserNameTakenError | InvalidUserNameError
+```
+
+There are three ways to map an exception to a user error.
+
+1. Map the exception directly
+2. Map with a factory method (`CreateErrorFrom`)
+3. Map with a constructor
+
+> Note: You can use AggregateExceptions to return multiple errors at once.
+
+### Map exceptions directly
+
+The quickest way to define a user error, is to map the exception directly into the graph. You can just annotate the exception directly on the resolver.
+If the exception is thrown and is caught in the error middleware, it will be rewritten into an user error that is exposed on the mutation payload.
+
+> The name of the exception will be rewritten. `Exception` is replaced with `Error` to follow the common GraphQL naming conventions.
+
+
+
+
+```csharp
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ : base($"The username {username} is already taken.")
+ {
+ }
+}
+
+public class Mutation
+{
+ [Error(typeof(UserNameTakenException))]
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ : base($"The username {username} is already taken.")
+ {
+ }
+}
+
+public class Mutation
+{
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ // ...
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default))
+ .Error();
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ : base($"The username {username} is already taken.")
+ {
+ }
+}
+```
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!): User
+}
+```
+
+```csharp
+public class Mutation
+{
+ [Error(typeof(UserNameTakenException))]
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+### Map with a factory method
+
+Often there is a need to control the error shape and ensure that not too many details are exposed. In these cases, we can use a custom error class representing the user error in our schema.
+
+The error instance and the translation of the exception can be done by an error factory. The error factory method receives an exception and returns the error object.
+
+Add a `public` `static` method called `CreateErrorFrom` that takes an exception and returns the error object.
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(string username)
+ {
+ Message = $"The username {username} is already taken.";
+ }
+
+ public static MyCustomError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public static MyCustomError CreateErrorFrom(OtherException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public string Message { get; }
+}
+
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ {
+ Username = username;
+ }
+
+ public string Username { get; }
+}
+
+public class Mutation
+{
+ [Error(typeof(UserNameTakenError))]
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(string username)
+ {
+ Message = $"The username {username} is already taken.";
+ }
+
+ public static MyCustomError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public static MyCustomError CreateErrorFrom(OtherException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public string Message { get; }
+}
+
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ {
+ Username = username;
+ }
+
+ public string Username { get; }
+}
+
+public class Mutation
+{
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ // ...
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default))
+ .Error();
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(string username)
+ {
+ Message = $"The username {username} is already taken.";
+ }
+
+ public static MyCustomError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public static MyCustomError CreateErrorFrom(OtherException ex)
+ {
+ return new MyCustomError(ex.Username);
+ }
+
+ public string Message { get; }
+}
+```
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!): User
+}
+```
+
+```csharp
+public class Mutation
+{
+ [Error(typeof(UserNameTakenError))]
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+Error factories can also be located in a dedicated class.
+
+```csharp
+public static class CreateUserErrorFactory
+{
+ public static MyCustomErrorA CreateErrorFrom(DomainExceptionA ex)
+ {
+ return new MyCustomError();
+ }
+
+ public static MyCustomErrorB CreateErrorFrom(DomainExceptionB ex)
+ {
+ return new MyCustomError();
+ }
+}
+
+public class Mutation
+{
+ [Error(typeof(CreateUserErrorFactory))]
+ public CreateUserPayload CreateUser(CreateUserInput input)
+ {
+ // ...
+ }
+}
+```
+
+Further the error factory methods do not have to be static.
+You can also use the `IPayloadErrorFactory` interface, to define instance error factory methods. This also enables you to use dependency injection with your factory class.
+
+```csharp
+public class CreateUserErrorFactory
+ : IPayloadErrorFactory
+ , IPayloadErrorFactory
+{
+ public MyCustomErrorA CreateErrorFrom(DomainExceptionA ex)
+ {
+ return new MyCustomError();
+ }
+
+ public MyCustomErrorB CreateErrorFrom(DomainExceptionB ex)
+ {
+ return new MyCustomError();
+ }
+}
+
+public class Mutation
+{
+ [Error(typeof(CreateUserErrorFactory))]
+ public CreateUserPayload CreateUser(CreateUserInput input)
+ {
+ // ...
+ }
+}
+```
+
+### Map with a constructor
+
+Lastly, we can also use the constructor of an error class to consume an exception. Essentially the constructor in this case represents the factory that we described earlier.
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(UserNameTakenException ex)
+ {
+ Message = $"The username {ex.Username} is already taken.";
+ }
+
+ public string Message { get; }
+}
+
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ {
+ Username = username;
+ }
+
+ public string Username { get; }
+}
+
+public class Mutation
+{
+ [Error(typeof(UserNameTakenError))]
+ public User? UpdateUserNameAsync([ID] Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(UserNameTakenException ex)
+ {
+ Message = $"The username {ex.Username} is already taken.";
+ }
+
+ public string Message { get; }
+}
+
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ {
+ Username = username;
+ }
+
+ public string Username { get; }
+}
+
+public class Mutation
+{
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ // ...
+ }
+}
+
+public class MutationType : ObjectType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.UpdateUserNameAsync(default))
+ .Error();
+ }
+}
+```
+
+
+
+
+```csharp
+public class UserNameTakenError
+{
+ private UserNameTakenError(UserNameTakenException ex)
+ {
+ Message = $"The username {ex.Username} is already taken.";
+ }
+
+ public string Message { get; }
+}
+
+public class UserNameTakenException : Exception
+{
+ public UserNameTakenException(string username)
+ {
+ Username = username;
+ }
+
+ public string Username { get; }
+}
+```
+
+```sdl
+type Mutation {
+ updateUserName(userId: ID!, username: String!): User
+}
+```
+
+```csharp
+public class Mutation
+{
+ [Error(typeof(UserNameTakenError))]
+ public User? UpdateUserNameAsync(Guid userId, string username)
+ {
+ //...
+ }
+}
+```
+
+
+
+
+> Note: errors and error factories can be shared between multiple mutations.
+
+## Customization
+
+While the mutation conventions strictly follow the outlined mutation and error patterns they still can be customized.
+
+### Naming
+
+The naming patterns for inputs, payloads and errors can be adjusted globally as well as on a per mutation basis.
+
+In order to change the global mutation naming patterns you can pass in the `MutationConventionOptions` into the `AddMutationConventions` configuration method.
+
+```csharp
+services
+ .AddGraphQL()
+ .AddMutationConventions(
+ new MutationConventionOptions
+ {
+ InputArgumentName = "input",
+ InputTypeNamePattern = "{MutationName}Input",
+ PayloadTypeNamePattern = "{MutationName}Payload",
+ PayloadErrorTypeNamePattern = "{MutationName}Error",
+ PayloadErrorsFieldName = "errors",
+ ApplyToAllMutations = true
+ })
+ ...
+```
+
+To override the global mutation settings on a mutation use the `UseMutationConvention` annotation.
+
+```csharp
+[UseMutationConvention(
+ InputTypeName = "FooInput",
+ InputArgumentName = "foo",
+ PayloadTypeName = "FooPayload",
+ PayloadFieldName = "bar")]
+public User? UpdateUserNameAsync(Guid userId, string username)
+{
+ //...
+}
+```
+
+### Opting Out
+
+Often we want to infer everything and only opt-out for exceptional cases, and the mutation convention allows us to do that in an effortless way.
+
+The first way to opt out of the global conventions is to use the `UseMutationConvention` annotation. With `UseMutationConvention` we can tell the type system initialization to disable the convention on certain mutations.
+
+```csharp
+[UseMutationConvention(Disable = true)]
+public User? UpdateUserNameAsync(Guid userId, string username)
+{
+ //...
+}
+```
+
+In many cases, we do not want to entirely opt-out but rather override the global settings since we wish for a more complex payload or input. We can simply add our own payload or input type in these cases, and the schema initialization will recognize that. Essentially if we follow the naming pattern for either input or payload, the initialization will not rewrite that part that already follows the global convention.
+
+```csharp
+public UpdateUserNamePayload UpdateUserNameAsync(UpdateUserNameInput input)
+{
+ //...
+}
+```
+
+You can also partially opt-out:
+
+```csharp
+public User UpdateUserNameAsync(UpdateUserNameInput input)
+{
+ //...
+}
+```
+
+### Custom error interface
+
+Lastly, we can customize the error interface we want to use with our mutation convention. The error interface is shared across all error types that the schema defines and provides the minimum shape that all errors have to fulfill.
+
+By default, this error interface type is called `Error` and defines a non-nullable field `message`.
+
+```sdl
+interface Error {
+ message: String!
+}
+```
+
+Often we also want to provide an error code so that the GUI components can more easily implement error handling logic. In such a case, we could provide our own error interface.
+
+> Note: All your error types have to implement the contract that the interface declares! Your errors/exceptions do not have to implement the common interface, but they have to declare all the interface's members.
+
+
+
+
+```csharp
+[GraphQLName("UserError")]
+public interface IUserError
+{
+ string Message { get; }
+
+ string Code { get; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ // ... Omitted code for brevity
+ .AddErrorInterfaceType();
+ }
+}
+```
+
+
+
+
+```csharp
+public class CustomErrorInterfaceType : InterfaceType
+{
+ protected override void Configure(IInterfaceTypeDescriptor descriptor)
+ {
+ descriptor.Name("UserError");
+ descriptor.Field("message").Type>();
+ descriptor.Field("code").Type>();
+ }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ // ... Omitted code for brevity
+ .AddErrorInterfaceType();
+ }
+}
+```
+
+
+
+
+```sdl
+interface UserError @errorInterface {
+ message: String!
+ code: String!
+}
+```
+
+
+
+
+```sdl
+interface UserError {
+ message: String!
+ code: String!
+}
+```
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/non-null.md b/website/src/docs/hotchocolate/v12/defining-a-schema/non-null.md
new file mode 100644
index 00000000000..e184e55695d
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/non-null.md
@@ -0,0 +1,155 @@
+---
+title: "Non-Null"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+Per default all fields on an object type can be either `null` or the specified type.
+
+```sdl
+type User {
+ name: String
+}
+```
+
+In the above example `name` can either be `null` or a `String`.
+
+Being nullable does not make sense for every field though. Maybe we have some database constraint which enforces the `name` to never be `null`.
+GraphQL allows us to be specific about this, by marking a field as non-null.
+
+```sdl
+type User {
+ name: String!
+}
+```
+
+The exclamation mark (`!`) denotes that the field can never be `null`.
+This is also enforced by the execution engine. If we were to return a `null` value in the `name` resolver, the execution engine would throw an error. This prevents unexpected `null` values from causing issues in the consuming applications.
+
+
+
+# Implicit nullability
+
+Hot Chocolate automatically infers the nullability of the schema type from the nullability of the used CLR type.
+
+[Value types](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/value-types) are non-null per default, unless they have been marked as nullable.
+
+| CLR Type | Schema Type |
+| ----------------------- | ----------- |
+| int | Int! |
+| int? | Int |
+| Nullable<int> | Int |
+
+[Reference types](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/reference-types) are always nullable, unless we have enabled [nullable reference types](https://docs.microsoft.com/dotnet/csharp/nullable-references). With nullable reference types enabled all fields are non-null per default.
+
+We strongly encourage the use of nullable reference types.
+
+# Explicit nullability
+
+We can also be explicit about the nullability of our fields.
+
+
+
+
+```csharp
+public class Query
+{
+ [GraphQLNonNullType]
+ public Book GetBook()
+ {
+ return new Book { Title = "C# in depth", Author = "Jon Skeet" };
+ }
+}
+
+public class Book
+{
+ [GraphQLNonNullType]
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetBook())
+ .Type>();
+ }
+}
+
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.Title)
+ .Type>();
+
+ descriptor
+ .Field(f => f.Author)
+ .Type();
+ }
+}
+```
+
+
+
+
+```sdl
+type Book {
+ title: String!
+ nullableTitle: String
+}
+```
+
+
+
+
+The inner type of a list can be made non-null like the following.
+
+
+
+
+```csharp
+public class Book
+{
+ [GraphQLType(typeof(ListType>))]
+ public List Genres { get; set; }
+}
+```
+
+
+
+
+```csharp
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.Genres)
+ .Type>>();
+ }
+}
+```
+
+
+
+
+```sdl
+type Book {
+ genres: [String!]
+}
+```
+
+
+
diff --git a/website/src/docs/hotchocolate/v12/defining-a-schema/object-types.md b/website/src/docs/hotchocolate/v12/defining-a-schema/object-types.md
new file mode 100644
index 00000000000..11d035efd51
--- /dev/null
+++ b/website/src/docs/hotchocolate/v12/defining-a-schema/object-types.md
@@ -0,0 +1,526 @@
+---
+title: "Object Types"
+---
+
+import { ExampleTabs, Annotation, Code, Schema } from "../../../components/mdx/example-tabs"
+
+The most important type in a GraphQL schema is the object type. It contains fields that can return simple scalars like `String`, `Int`, or again object types.
+
+```sdl
+type Author {
+ name: String
+}
+
+type Book {
+ title: String
+ author: Author
+}
+```
+
+Learn more about object types [here](https://graphql.org/learn/schema/#object-types-and-fields).
+
+# Definition
+
+Object types can be defined like the following.
+
+
+
+
+In the Annotation-based approach we are essentially just creating regular C# classes.
+
+```csharp
+public class Author
+{
+ public string Name { get; set; }
+}
+```
+
+
+
+
+In the Code-first approach we create a new class inheriting from `ObjectType` to map our POCO `Author` to an object type.
+
+```csharp
+public class Author
+{
+ public string Name { get; set; }
+}
+
+public class AuthorType : ObjectType
+{
+}
+```
+
+We can override the `Configure` method to have access to an `IObjectTypeDescriptor` through which we can configure the object type.
+
+```csharp
+public class AuthorType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+
+ }
+}
+```
+
+The `IObjectTypeDescriptor` gives us the ability to configure the object type. We will cover how to use it in the following chapters.
+
+Since there could be multiple types inheriting from `ObjectType`, but differing in their name and fields, it is not certain which of these types should be used when we return an `Author` CLR type from one of our resolvers.
+
+**Therefore it's important to note that Code-first object types are not automatically inferred. They need to be explicitly specified or registered.**
+
+We can either [explicitly specify the type on a per-resolver basis](#explicit-types) or we can register the type once globally:
+
+```csharp
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddType();
+ }
+}
+```
+
+With this configuration every `Author` CLR type we return from our resovlers would be assumed to be an `AuthorType`.
+
+We can also create schema object types without a backing POCO.
+
+```csharp
+public class AuthorType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+
+ }
+}
+```
+
+Head over [here](#additional-fields) to learn how to add fields to such a type.
+
+
+
+
+```csharp
+public class Author
+{
+ public string Name { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Author {
+ name: String
+ }
+ ")
+ .BindRuntimeType();
+ }
+}
+```
+
+
+
+
+# Binding behavior
+
+In the Annotation-based approach all public properties and methods are implicitly mapped to fields on the schema object type. The same is true for `T` of `ObjectType` when using the Code-first approach.
+
+In the Code-first approach we can also enable explicit binding, where we have to opt-in properties and methods we want to include instead of them being implicitly included.
+
+
+
+We can configure our preferred binding behavior globally like the following.
+
+```csharp
+services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> ⚠️ Note: This changes the binding behavior for all types, not only object types.
+
+We can also override it on a per type basis:
+
+```csharp
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.BindFields(BindingBehavior.Implicit);
+
+ // We could also use the following methods respectively
+ // descriptor.BindFieldsExplicitly();
+ // descriptor.BindFieldsImplicitly();
+ }
+}
+```
+
+## Ignoring fields
+
+
+
+
+In the Annotation-based approach we can ignore fields using the `[GraphQLIgnore]` attribute.
+
+```csharp
+public class Book
+{
+ [GraphQLIgnore]
+ public string Title { get; set; }
+
+ public Author Author { get; set; }
+}
+```
+
+
+
+
+In the Code-first approach we can ignore fields of our POCO using the `Ignore` method on the `IObjectTypeDescriptor`. This is only necessary, if the binding behavior of the object type is implicit.
+
+```csharp
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Ignore(f => f.Title);
+ }
+}
+```
+
+
+
+
+We do not have to ignore fields in the Schema-first approach.
+
+
+
+
+## Including fields
+
+In the Code-first approach we can explicitly include properties of our POCO using the `Field` method on the `IObjectTypeDescriptor`. This is only necessary, if the binding behavior of the object type is explicit.
+
+```csharp
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+
+ descriptor.Field(f => f.Title);
+ }
+}
+```
+
+# Naming
+
+Unless specified explicitly, Hot Chocolate automatically infers the names of object types and their fields. Per default the name of the class becomes the name of the object type. When using `ObjectType` in Code-first, the name of `T` is chosen as the name for the object type. The names of methods and properties on the respective class are chosen as names of the fields of the object type.
+
+The following conventions are applied when transforming C# method and property names into SDL types and fields:
+
+- **Get prefixes are removed:** The get operation is implied and therefore redundant information.
+- **Async postfixes are removed:** The `Async` is an implementation detail and therefore not relevant to the schema.
+- **The first letter is lowercased:** This is not part of the specification, but a widely agreed upon standard in the GraphQL world.
+
+If we need to we can override these inferred names.
+
+
+
+
+The `[GraphQLName]` attribute allows us to specify an explicit name.
+
+```csharp
+[GraphQLName("BookAuthor")]
+public class Author
+{
+ [GraphQLName("fullName")]
+ public string Name { get; set; }
+}
+```
+
+
+
+
+The `Name` method on the `IObjectTypeDescriptor` / `IObjectFieldDescriptor` allows us to specify an explicit name.
+
+```csharp
+public class AuthorType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("BookAuthor");
+
+ descriptor
+ .Field(f => f.Name)
+ .Name("fullName");
+ }
+}
+```
+
+
+
+
+Simply change the names in the schema.
+
+
+
+
+This would produce the following `BookAuthor` schema object type:
+
+```sdl
+type BookAuthor {
+ fullName: String
+}
+```
+
+If only one of our clients requires specific names, it is better to use [aliases](https://graphql.org/learn/queries/#aliases) in this client's operations than changing the entire schema.
+
+```graphql
+{
+ MyUser: user {
+ Username: name
+ }
+}
+```
+
+# Explicit types
+
+Hot Chocolate will, most of the time, correctly infer the schema types of our fields. Sometimes we might have to be explicit about it though. For example when we are working with custom scalars or Code-first types in general.
+
+
+
+
+In the annotation-based approach we can use the `[GraphQLType]` attribute.
+
+```csharp
+public class Author
+{
+ [GraphQLType(typeof(StringType))]
+ public string Name { get; set; }
+}
+```
+
+
+
+
+In the Code-first approach we can use the `Type` method on the `IObjectFieldDescriptor`.
+
+```csharp
+public class AuthorType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.Name)
+ .Type();
+ }
+}
+```
+
+
+
+
+Simply change the field type in the schema.
+
+
+
+
+# Additional fields
+
+We can add additional (dynamic) fields to our schema types, without adding new properties to our backing class.
+
+
+
+
+```csharp
+public class Author
+{
+ public string Name { get; set; }
+
+ public DateTime AdditionalField()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+In the Code-first approach we can use the `Resolve` method on the `IObjectFieldDescriptor`.
+
+```csharp
+public class AuthorType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("AdditionalField")
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ })
+ }
+}
+```
+
+
+
+
+```csharp
+public class Author
+{
+ public string Name { get; set; }
+}
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Author {
+ name: String
+ additionalField: DateTime!
+ }
+ ")
+ .BindRuntimeType()
+ .AddResolver("Author", "additionalField", (context) =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+What we have just created is a resolver. Hot Chocolate automatically creates resolvers for our properties, but we can also define them ourselves.
+
+[Learn more about resolvers](/docs/hotchocolate/v12/fetching-data/resolvers)
+
+# Generics
+
+> Note: Read about [interfaces](/docs/hotchocolate/v12/defining-a-schema/interfaces) and [unions](/docs/hotchocolate/v12/defining-a-schema/unions) before resorting to generic object types.
+
+In the Code-first approach we can define generic object types.
+
+```csharp
+public class Response
+{
+ public string Status { get; set; }
+
+ public object Payload { get; set; }
+}
+
+public class ResponseType : ObjectType
+ where T : class, IOutputType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(f => f.Status);
+
+ descriptor
+ .Field(f => f.Payload)
+ .Type();
+ }
+}
+
+public class Query
+{
+ public Response GetResponse()
+ {
+ return new Response
+ {
+ Status = "OK",
+ Payload = 123
+ };
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetResponse())
+ .Type>();
+ }
+}
+```
+
+This will produce the following schema types.
+
+```sdl
+type Query {
+ response: Response
+}
+
+type Response {
+ status: String!
+ payload: Int
+}
+```
+
+We have used an `object` as the generic field above, but we can also make `Response` generic and add another generic parameter to the `ResponseType`.
+
+```csharp
+public class Response
+{
+ public string Status { get; set; }
+
+ public T Payload { get; set; }
+}
+
+public class ResponseType
+ : ObjectType>
+ where TSchemaType : class, IOutputType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor> descriptor)
+ {
+ descriptor.Field(f => f.Status);
+
+ descriptor
+ .Field(f => f.Payload)
+ .Type();
+ }
+}
+```
+
+## Naming
+
+If we were to use the above type with two different generic arguments, we would get an error, since both `ResponseType` have the same name.
+
+We can change the name of our generic object type depending on the used generic type.
+
+```csharp
+public class ResponseType : ObjectType