diff --git a/website/src/docs/docs.json b/website/src/docs/docs.json
index 7d22c543fa1..dc0b4737a7f 100644
--- a/website/src/docs/docs.json
+++ b/website/src/docs/docs.json
@@ -155,6 +155,340 @@
"metaDescription": "Hot Chocolate is the most efficient, feature-rich, open-source GraphQL server in the .NET ecosystem, that helps developers to build powerful APIs.",
"latestStableVersion": "v14",
"versions": [
+ {
+ "path": "v15",
+ "title": "v15",
+ "items": [
+ {
+ "path": "index",
+ "title": "Introduction"
+ },
+ {
+ "path": "get-started-with-graphql-in-net-core",
+ "title": "Getting 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": "dynamic-schemas",
+ "title": "Dynamic Schemas"
+ }
+ ]
+ },
+ {
+ "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": "spatial-data",
+ "title": "Spatial Data"
+ },
+ {
+ "path": "marten",
+ "title": "Marten"
+ }
+ ]
+ },
+ {
+ "path": "server",
+ "title": "Server",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "endpoints",
+ "title": "Endpoints"
+ },
+ {
+ "path": "http-transport",
+ "title": "HTTP transport"
+ },
+ {
+ "path": "interceptors",
+ "title": "Interceptors"
+ },
+ {
+ "path": "dependency-injection",
+ "title": "Dependency injection"
+ },
+ {
+ "path": "global-state",
+ "title": "Global State"
+ },
+ {
+ "path": "introspection",
+ "title": "Introspection"
+ },
+ {
+ "path": "files",
+ "title": "Files"
+ },
+ {
+ "path": "instrumentation",
+ "title": "Instrumentation"
+ },
+ {
+ "path": "batching",
+ "title": "Batching"
+ },
+ {
+ "path": "command-line",
+ "title": "Command Line"
+ }
+ ]
+ },
+ {
+ "path": "fusion",
+ "title": "Fusion",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ }
+ ]
+ },
+ {
+ "path": "performance",
+ "title": "Performance",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "persisted-operations",
+ "title": "Persisted operations"
+ },
+ {
+ "path": "automatic-persisted-operations",
+ "title": "Automatic persisted operations"
+ }
+ ]
+ },
+ {
+ "path": "security",
+ "title": "Security",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "authentication",
+ "title": "Authentication"
+ },
+ {
+ "path": "authorization",
+ "title": "Authorization"
+ },
+ {
+ "path": "cost-analysis",
+ "title": "Cost Analysis"
+ }
+ ]
+ },
+ {
+ "path": "api-reference",
+ "title": "API Reference",
+ "items": [
+ {
+ "path": "custom-attributes",
+ "title": "Custom Attributes"
+ },
+ {
+ "path": "errors",
+ "title": "Errors"
+ },
+ {
+ "path": "language",
+ "title": "Language"
+ },
+ {
+ "path": "extending-filtering",
+ "title": "Extending Filtering"
+ },
+ {
+ "path": "visitors",
+ "title": "Visitors"
+ },
+ {
+ "path": "apollo-federation",
+ "title": "Apollo Federation"
+ },
+ {
+ "path": "executable",
+ "title": "Executable"
+ }
+ ]
+ },
+ {
+ "path": "migrating",
+ "title": "Migrating",
+ "items": [
+ {
+ "path": "migrate-from-13-to-14",
+ "title": "Migrate from 13 to 14"
+ },
+ {
+ "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 to 11"
+ }
+ ]
+ }
+ ]
+ },
{
"path": "v14",
"title": "v14",
@@ -1658,6 +1992,106 @@
"metaDescription": "Strawberry Shake is an incredible GraphQL client for the .NET ecosystem, that helps developers to build awesome UIs in Blazor, Maui, and more.",
"latestStableVersion": "v14",
"versions": [
+ {
+ "path": "v15",
+ "title": "v15",
+ "items": [
+ {
+ "path": "index",
+ "title": "Introduction"
+ },
+ {
+ "path": "get-started",
+ "title": "Get Started",
+ "items": [
+ {
+ "path": "index",
+ "title": "Blazor"
+ },
+ {
+ "path": "xamarin",
+ "title": "Xamarin"
+ },
+ {
+ "path": "console",
+ "title": "Console"
+ }
+ ]
+ },
+ {
+ "path": "subscriptions",
+ "title": "Subscriptions"
+ },
+ {
+ "path": "tooling",
+ "title": "Tooling / CLI"
+ },
+ {
+ "path": "caching",
+ "title": "Caching",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "entities",
+ "title": "Entities"
+ },
+ {
+ "path": "invalidation",
+ "title": "Invalidation"
+ }
+ ]
+ },
+ {
+ "path": "performance",
+ "title": "Performance",
+ "items": [
+ {
+ "path": "index",
+ "title": "Overview"
+ },
+ {
+ "path": "persisted-operations",
+ "title": "Persisted Operations"
+ },
+ {
+ "path": "persisted-state",
+ "title": "Persisted State"
+ }
+ ]
+ },
+ {
+ "path": "networking",
+ "title": "Networking",
+ "items": [
+ {
+ "path": "index",
+ "title": "Protocols"
+ },
+ {
+ "path": "authentication",
+ "title": "Authentication"
+ }
+ ]
+ },
+ {
+ "path": "scalars",
+ "title": "Scalars"
+ },
+ {
+ "path": "migrating",
+ "title": "Migrating",
+ "items": [
+ {
+ "path": "migrate-from-12-to-13",
+ "title": "Migrate from 12 to 13"
+ }
+ ]
+ }
+ ]
+ },
{
"path": "v14",
"title": "v14",
diff --git a/website/src/docs/hotchocolate/v15/api-reference/apollo-federation.md b/website/src/docs/hotchocolate/v15/api-reference/apollo-federation.md
new file mode 100644
index 00000000000..8e410fe67f7
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/api-reference/apollo-federation.md
@@ -0,0 +1,716 @@
+---
+title: Apollo Federation Subgraph Support
+---
+
+> If you want to read more about Apollo Federation in general, you can head over to the [Apollo Federation documentation](https://www.apollographql.com/docs/federation/), which provides a robust overview and set of examples for this GraphQL architectural pattern. Many of the core principles and concepts are referenced within this document.
+
+Hot Chocolate includes an implementation of the Apollo Federation v1 specification for creating Apollo Federated subgraphs. Through Apollo Federation, you can combine multiple GraphQL APIs into a single API for your consumers.
+
+The documentation describes the syntax for creating an Apollo Federated subgraph using Hot Chocolate and relates the implementation specifics to its counterpart in the Apollo Federation docs. This document _will not_ provide a thorough explanation of the Apollo Federation core concepts nor will it describe how you go about creating a supergraph to stitch together various subgraphs, as the Apollo Federation team already provides thorough documentation of those principles.
+
+You can find example projects of the Apollo Federation library in [Hot Chocolate examples](https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/ApolloFederation/examples).
+
+# Get Started
+
+To use the Apollo Federation tools, you need to first install v12.6 or later of the `HotChocolate.ApolloFederation` package.
+
+
+
+After installing the necessary package, you'll need to register the Apollo Federation services with the GraphQL server.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation();
+```
+
+# Defining an entity
+
+Now that the API is ready to support Apollo Federation, we'll need to define an **entity**—an object type that can resolve its fields across multiple subgraphs. We'll work with a `Product` entity to provide an example of how to do this.
+
+
+
+
+```csharp
+public class Product
+{
+ [ID]
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+}
+```
+
+
+
+
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(product => product.Id).ID();
+ }
+}
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+## Define an entity key
+
+Once we have an object type to work with, we'll [define a key](https://www.apollographql.com/docs/federation/entities#1-define-a-key) for the entity. A key in an Apollo Federated subgraph effectively serves as an "identifier" that can uniquely locate an individual record of that type. This will typically be something like a record's primary key, a SKU, or an account number.
+
+
+
+
+
+In an implementation-first approach, we'll use the `[Key]` attribute on any property or properties that can be referenced as a key by another subgraph.
+
+```csharp
+public class Product
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+}
+```
+
+
+
+
+
+In a code-first approach, we'll use the `Key()` method to designate any GraphQL fields that can be reference as a key by another subgraph.
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(product => product.Id).ID();
+
+ // Matches the Id property when it is converted to the GraphQL schema
+ descriptor.Key("id");
+ }
+}
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+
+## Define a reference resolver
+
+Next, we'll need to define an [entity reference resolver](https://www.apollographql.com/docs/federation/entities#2-define-a-reference-resolver) so that the supergraph can resolve this entity across multiple subgraphs during a query. Every subgraph that contributes at least one unique field to an entity must define a reference resolver for that entity.
+
+
+
+
+
+In an implementation-first approach, a reference resolver will work just like a [regular resolver](/docs/hotchocolate/v15/fetching-data/resolvers) with some key differences:
+
+1. It must be annotated with the `[ReferenceResolver]` attribute
+1. It must be a `public static` method _within_ the type it is resolving
+
+```csharp
+public class Product
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+
+ [ReferenceResolver]
+ public static async Task ResolveReference(
+ // Represents the value that would be in the Id property of a Product
+ string id,
+ // Example of a service that can resolve the Products
+ ProductBatchDataLoader dataLoader
+ )
+ {
+ return await dataloader.LoadAsync(id);
+ }
+}
+```
+
+Some important details to highlight about `[ReferenceResolver]` methods.
+
+1. The name of the method decorated with the `[ReferenceResolver]` attribute does not matter. However, as with all programming endeavors, you should aim to provide a descriptive name that reveals the method's intention.
+1. The parameter name and type used in the reference resolver **must match** the GraphQL field name of the `[Key]` attribute, e.g., if the GraphQL key field is `id: String!` or `id: ID!` then the reference resolver's parameter must be `string id`.
+1. If you're using [nullable reference types](https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references), you should make sure the return type is marked as possibly null, i.e., `T?`.
+1. If you have multiple keys defined for an entity, you should include a reference resolver for _each key_ so that the supergraph is able to resolve your entity regardless of which key(s) another graph uses to reference that entity.
+
+```csharp
+public class Product
+{
+ [Key]
+ public string Id { get; set; }
+
+ [Key]
+ public int Sku { get; set; }
+
+ [ReferenceResolver]
+ public static Product? ResolveReferenceById(string id)
+ {
+ // Locates the Product by its Id.
+ }
+
+ [ReferenceResolver]
+ public static Product? ResolveReferenceBySku(int sku)
+ {
+ // Locates the product by SKU
+ }
+}
+```
+
+
+
+
+
+We'll now chain a `ResolveReferenceWith()` method call off of the `Key()` method call from the previous step. This will create a [resolver](/docs/hotchocolate/v15/fetching-data/resolvers) that the Hot Chocolate engine can invoke.
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+
+ public string Name { get; set; }
+
+ public float Price { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(product => product.Id).ID();
+
+ descriptor.Key("id")
+ .ResolveReferenceWith(_ => ResolveByIdAsync(default!, default!));
+ }
+
+ private static Task ResolveByIdAsync(
+ // Represents the value that would be in the Id property of a Product
+ string id,
+ // Example of a service that can resolve the Products
+ ProductBatchDataLoader dataLoader)
+ {
+ return await dataLoader.LoadAsync(id);
+ }
+}
+```
+
+Some important details to highlight about entity reference resolvers.
+
+1. The parameter name and type used in the reference resolver **must match** the GraphQL field name of the `Key()` field set, e.g., if the GraphQL key field is `id: String!` or `id: ID!` then the reference resolver's parameter must be `string id`.
+1. If you're using [nullable reference types](https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references), you should make sure the return type is marked as possibly null, i.e., `T?`.
+1. For each call to the `Key()` method, you should include a reference resolver so that the supergraph is able to resolve your entity regardless of which key(s) another graph uses to reference that entity.
+
+```csharp
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Key("id")
+ .ResolveReferenceWith(_ => ResolveByIdAsync(default!));
+
+ descriptor.Key("sku")
+ .ResolveReferenceWith(_ => ResolveBySkuAsync(default!))
+ }
+
+ private static Task ResolveByIdAsync(string id)
+ {
+ // Locate the product by its Id
+ }
+
+ private static Task ResolveBySkuAsync(default!)
+ {
+ // Locate the product by its SKU instead
+ }
+}
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+> ### A note about reference resolvers
+>
+> It's recommended to use a [dataloader](/docs/hotchocolate/v15/fetching-data/dataloader) to fetch the data in a reference resolver. This helps the API avoid [an N+1 problem](https://www.apollographql.com/docs/federation/entities-advanced#handling-the-n1-problem) when a query resolves multiple items from a given subgraph.
+
+## Register the entity
+
+After our type has a key or keys and a reference resolver defined, you'll register the type in the GraphQL schema, which will register it as a type within the GraphQL API itself as well as within the [auto-generated `_service { sdl }` field](https://www.apollographql.com/docs/federation/subgraph-spec/#required-resolvers-for-introspection) within the API.
+
+_Entity type registration_
+
+
+
+
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType()
+ // other registrations...
+ ;
+```
+
+
+
+
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType()
+ // other registrations...
+ ;
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+## Testing and executing your reference resolvers
+
+After creating an entity, you'll likely wonder "how do I invoke and test this reference resolver?" Entities that define a reference resolver can be queried through the [auto-generated `_entities` query](https://www.apollographql.com/docs/federation/subgraph-spec#understanding-query_entities) at the subgraph level.
+
+You'll invoke the query by providing an array of representations using a combination of a `__typename` and key field values to invoke the appropriate resolver. An example query for our `Product` would look something like the following.
+
+_Entities query_
+
+```graphql
+query {
+ _entities(
+ representations: [
+ { __typename: "Product", id: "" }
+ # You can provide multiple representations for multiple objects and types in the same query
+ ]
+ ) {
+ ... on Product {
+ id
+ name
+ price
+ }
+ }
+}
+```
+
+_Entities query result_
+
+```json
+{
+ "data": {
+ "_entities": [
+ {
+ "id": "",
+ "name": "Foobar",
+ "price": 10.99
+ }
+ // Any other values that were found, or null
+ ]
+ }
+}
+```
+
+> **Note**: The `_entities` field is an internal implementation detail of Apollo Federation that is necessary for the supergraph to properly resolve entities. API consumers **should not** use the `_entities` field directly nor should they send requests to a subgraph directly. We're only highlighting how to use the `_entities` field so that you can validate and test your subgraph and its entity reference resolvers at runtime or using tools like [`Microsoft.AspNetCore.Mvc.Testing`](https://learn.microsoft.com/aspnet/core/test/integration-tests).
+
+# Referencing an entity type
+
+Now that we have an entity defined in one of our subgraphs, let's go ahead and create a second subgraph that will make use of our `Product` type. Remember, all of this work should be performed in a _**separate API project**_.
+
+In the second subgraph, we'll create a `Review` type that is focused on providing reviews of `Product` entities from the other subgraph. We'll do that by defining our `Review` type along with a [service type reference](https://www.apollographql.com/docs/federation/entities/#referencing-an-entity-without-contributing-fields) that represents the `Product`.
+
+In our new subgraph API we'll need to start by creating the `Product`. When creating the extended service type, make sure to consider the following details
+
+- The _GraphQL type name_ **must match**. Often, this can be accomplished by using the same class name between the projects, but you can also use tools like the `[GraphQLName(string)]` attribute or `IObjectTypeDescriptor.Name(string)` method to explicitly set a GraphQL name.
+- The extended type must include _at least one_ key that matches in both name and GraphQL type from the source graph.
+ - In our example, we'll be referencing the `id: ID!` field that was defined on our `Product`
+
+
+
+
+
+```csharp
+[ExtendServiceType]
+public class Product
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+}
+
+// In your Program
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType();
+```
+
+
+
+
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.ExtendServiceType();
+
+ descriptor.Key("id");
+ descriptor.Field(product => product.Id).ID();
+ }
+}
+
+// In your Program
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType();
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+
+Next, we'll create our `Review` type that has a reference to the `Product` entity. Similar to our first class, we'll need to denote the type's key(s) and the corresponding entity reference resolver(s).
+
+
+
+
+
+```csharp
+public class Review
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+
+ public string Content { get; set; }
+
+ [GraphQLIgnore]
+ public string ProductId { get; set; }
+
+ public Product GetProduct() => new Product { Id = ProductId };
+
+ [ReferenceResolver]
+ public static Review? ResolveReference(string id)
+ {
+ // Omitted for brevity; some kind of service to retrieve the review.
+ }
+}
+
+// In your Program
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType()
+ .AddType();
+```
+
+
+
+
+
+```csharp
+public class Review
+{
+ public string Id { get; set; }
+
+ public string Content { get; set; }
+
+ public string ProductId { get; set; }
+
+ public Product GetProduct() => new Product { Id = ProductId };
+}
+
+public class ReviewType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Key("id").ResolveReferenceWith(_ => ResolveReviewById(default!));
+ descriptor.Field(review => review.Id).ID();
+
+ descriptor.Ignore(review => review.ProductId);
+ }
+
+ private static Review? ResolveReviewById(string id)
+ {
+ // Omitted for brevity
+ }
+}
+
+// In your Program
+builder.Services
+ .AddGraphQLServer()
+ .AddApolloFederation()
+ .AddType()
+ .AddType();
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+
+In the above snippet two things may pop out as strange to you:
+
+1. Why did we explicitly ignore the `ProductId` property?
+ - The `ProductId` is, in essence, a "foreign key" to the other graph. Instead of presenting that data as a field of the `Review` type, we're presenting it through the `product: Product!` GraphQL field that is produced by the `GetProduct()` method. This allows the Apollo supergraph to stitch the `Review` and `Product` types together and represent that a query can traverse from the `Review` to the `Product` it is reviewing and make the API more graph-like. With that said, it is not strictly necessary to ignore the `ProductId` or any other external entity Id property.
+2. Why does the `GetProduct()` method instantiate its own `new Product { Id = ProductId }` object?
+ - Since our goal with Apollo Federation is decomposition and [concern-based separation](https://www.apollographql.com/docs/federation/#concern-based-separation), a second subgraph is likely to have that "foreign key" reference to the type that is reference from the other subgraph. However, this graph does not "own" the actual data of the entity itself. This is why our sample simply performs a `new Product { Id = ProductId }` statement for the resolver: it's not opinionated about how the other data of a `Product` is resolved from its owning graph.
+
+With our above changes, we can successfully connect these two subgraphs into a single query within an Apollo supergraph, allowing our API users to send a query like the following.
+
+```graphql
+query {
+ # Example - not explicitly defined in our tutorial
+ review(id: "") {
+ id
+ content
+ product {
+ id
+ name
+ }
+ }
+}
+```
+
+As a reminder, you can create and configure a supergraph by following either the [Apollo Router documentation](https://www.apollographql.com/docs/router/quickstart/) or [`@apollo/gateway` documentation](https://www.npmjs.com/package/@apollo/gateway).
+
+## Contributing fields through resolvers
+
+Now that our new subgraph has the `Product` reference we can [contribute additional fields to the type](https://www.apollographql.com/docs/federation/entities#contributing-entity-fields). Similar to other types in Hot Chocolate, you can create new fields by defining different method or property resolvers. For a full set of details and examples on creating resolvers, you can read our [documentation on resolvers](/docs/hotchocolate/v15/fetching-data/resolvers).
+
+For now, we'll focus on giving our supergraph the ability to retrieve all reviews for a given product by adding a `reviews: [Review!]!` property to the type.
+
+
+
+
+
+```csharp
+[ExtendServiceType]
+public class Product
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+
+ public async Task> GetReviews(
+ ReviewRepository repo // example of how you might resolve this data
+ )
+ {
+ return await repo.GetReviewsByProductIdAsync(Id);
+ }
+}
+```
+
+
+
+
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+
+ public async Task> GetReviews(
+ ReviewRepository repo // example of how you might resolve this data
+ )
+ {
+ return await repo.GetReviewsByProductIdAsync(Id);
+ }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.ExtendServiceType();
+
+ descriptor.Key("id");
+ descriptor.Field(product => product.Id).ID();
+ }
+}
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+
+These changes will successfully add the new field within the subgraph! However, our current implementation cannot be resolved if we start at a product such as `query { product(id: "foo") { reviews { ... } } }`. To fix this, we'll need to implement an entity reference resolver in our second subgraph.
+
+As mentioned above, since this subgraph does not "own" the data for a `Product`, our resolver will be fairly naive, similar to the `Review::GetProduct()` method: it will simply instantiate a `new Product { Id = id }`. We do this because the reference resolver should only be directly invoked by the supergraph, so our new reference resolver will simply assume the data exists. However, if there is data that needs to be fetched from some kind of data store, the resolver can still do this just as any other data resolver in Hot Chocolate.
+
+
+
+
+
+```csharp
+[ExtendServiceType]
+public class Product
+{
+ [ID]
+ [Key]
+ public string Id { get; set; }
+
+ public async Task> GetReviews(
+ ReviewRepository repo // example of how you might resolve this data
+ )
+ {
+ return await repo.GetReviewsByProductIdAsync(Id);
+ }
+
+ [ReferenceResolver]
+ public static Product ResolveProductReference(string id) => new Product { Id = id };
+}
+```
+
+
+
+
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+
+ public async Task> GetReviews(
+ ReviewRepository repo // example of how you might resolve this data
+ )
+ {
+ return await repo.GetReviewsByProductIdAsync(Id);
+ }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.ExtendServiceType();
+
+ descriptor.Key("id").ResolveReferenceWith(_ => ResolveProductReference(default!));
+ descriptor.Field(product => product.Id).ID();
+ }
+
+ private static Product ResolveProductReference(string id) => new Product { Id = id };
+}
+```
+
+
+
+
+
+**Coming soon**
+
+
+
+
+
+With the above changes, our supergraph can now support traversing both "from a review to a product" as well as "from a product to a review"!
+
+```graphql
+# Example root query fields - not implemented in the tutorial
+query {
+ # From a review to a product (back to the reviews)
+ review(id: "foo") {
+ id
+ content
+ product {
+ id
+ name
+ price
+ reviews {
+ id
+ content
+ }
+ }
+ }
+ # From a product to a review
+ product(id: "bar") {
+ id
+ name
+ price
+ reviews {
+ id
+ content
+ }
+ }
+}
+```
diff --git a/website/src/docs/hotchocolate/v15/api-reference/custom-attributes.md b/website/src/docs/hotchocolate/v15/api-reference/custom-attributes.md
new file mode 100644
index 00000000000..98878bf95c0
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/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()
+ {
+ ...
+ }
+}
+```
+
+> Warning: 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()
+ {
+ ...
+ }
+}
+```
+
+> Warning: 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/v15/api-reference/custom-context-data.md b/website/src/docs/hotchocolate/v15/api-reference/custom-context-data.md
new file mode 100644
index 00000000000..c18f5084ec1
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/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 `IOperationRequestInterceptor` 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/v15/api-reference/errors.md b/website/src/docs/hotchocolate/v15/api-reference/errors.md
new file mode 100644
index 00000000000..c5d11a5a859
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/api-reference/errors.md
@@ -0,0 +1,71 @@
+---
+title: Errors
+---
+
+GraphQL errors in Hot Chocolate are passed to the operation result by returning an instance of `IError` or an enumerable of `IError` in a field resolver.
+
+Moreover, you can throw a `GraphQLException` that will be be caught by the execution 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.ReportError`. 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/v15/server/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, we have introduced a new error builder which provides a nice API without thousands of overloads.
+
+```csharp
+var error = ErrorBuilder
+ .New()
+ .SetMessage("This is my error.")
+ .SetCode("FOO_BAR")
+ .Build();
+```
+
+# Error Filters
+
+If some other exception is thrown during 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 as a service.
+
+```csharp
+builder.Services.AddErrorFilter();
+```
+
+It is also possible to just register the error filter as a delegate like the following.
+
+```csharp
+builder.Services.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 errors, you can enable the `IncludeExceptionDetails` option. By default this will be enabled when the debugger is attached.
+
+```csharp
+builder
+ .AddGraphQL()
+ .ModifyRequestOptions(
+ o => o.IncludeExceptionDetails =
+ builder.Environment.IsDevelopment());
+```
diff --git a/website/src/docs/hotchocolate/v15/api-reference/executable.md b/website/src/docs/hotchocolate/v15/api-reference/executable.md
new file mode 100644
index 00000000000..a339851f0cd
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/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 IUserRepository
+{
+ public IExecutable FindAll();
+}
+
+public class Query
+{
+ public IExecutable GetUsers(IUserRepository 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 EntityFramework 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/v15/api-reference/extending-filtering.md b/website/src/docs/hotchocolate/v15/api-reference/extending-filtering.md
new file mode 100644
index 00000000000..f2ebde3ae27
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/api-reference/extending-filtering.md
@@ -0,0 +1,424 @@
+---
+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/v15/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)
+ {
+ descriptor.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/v15/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 traversal 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 `IQueryable` 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 property 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 property 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 property 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 property 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 implementation 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
+{
+ public QueryableStringInvariantEqualsHandler(InputParser inputParser) : base(inputParser)
+ {
+ }
+
+ // 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 property
+ // 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 returns 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
+builder.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
+builder.Services
+ .AddGraphQLServer()
+ .AddFiltering()
+ .AddConvention(
+ new FilterConventionExtension(
+ x => x.AddProviderExtension(
+ new QueryableFilterProviderExtension(
+ y => y.AddFieldHandler()))));
+```
diff --git a/website/src/docs/hotchocolate/v15/api-reference/language.md b/website/src/docs/hotchocolate/v15/api-reference/language.md
new file mode 100644
index 00000000000..42359107510
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/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 ...](https://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.](https://spec.graphql.org/June2018/#sec-Language.Document) | Query (out) | |
+| OperationDefinition | [Describes a graphql operation like `query` `mutation` or `subscription`](https://spec.graphql.org/June2018/#sec-Language.Document) | Query (out) | query Foo {} |
+| VariableDefinition | [The variables defined by an operation](https://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_](https://spec.graphql.org/June2018/#sec-Selection-Sets) | Query (out) | {foo bar} |
+| Field | [Describes a field as a part of a selection set](https://spec.graphql.org/June2018/#sec-Language.Fields) | Query (out) | foo |
+| FragmentSpread | [Denotes a spread of a `FragmentDefinition`](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-definition](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/v15/api-reference/options.md b/website/src/docs/hotchocolate/v15/api-reference/options.md
new file mode 100644
index 00000000000..b804ce02085
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/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 behavior. The options can be set during schema creation.
+
+```csharp
+SchemaBuilder.New()
+ .ModifyOptions(opt =>
+ {
+ opt.QueryTypeName = "Foo";
+ })
+ ...
+ .Create()
+```
diff --git a/website/src/docs/hotchocolate/v15/api-reference/visitors.md b/website/src/docs/hotchocolate/v15/api-reference/visitors.md
new file mode 100644
index 00000000000..d9b7292a4b3
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/api-reference/visitors.md
@@ -0,0 +1,151 @@
+---
+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/v15/defining-a-schema/arguments.md b/website/src/docs/hotchocolate/v15/defining-a-schema/arguments.md
new file mode 100644
index 00000000000..6fc387e7aa6
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/arguments.md
@@ -0,0 +1,122 @@
+---
+title: "Arguments"
+---
+
+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
+builder.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/v15/defining-a-schema/non-null)
+
+If we need to provide complex objects as arguments, we can use [input object types](/docs/hotchocolate/v15/defining-a-schema/input-object-types).
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/directives.md b/website/src/docs/hotchocolate/v15/defining-a-schema/directives.md
new file mode 100644
index 00000000000..e5eddfb5b2e
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/directives.md
@@ -0,0 +1,412 @@
+---
+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
+builder.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 separated 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, directive) => 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/v15/defining-a-schema/documentation.md b/website/src/docs/hotchocolate/v15/defining-a-schema/documentation.md
new file mode 100644
index 00000000000..cee76adf96b
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/documentation.md
@@ -0,0 +1,245 @@
+---
+title: Documentation
+---
+
+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
+builder.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
+builder.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) { }
+}
+
+// Program
+// Before
+.AddConvention(sp => new CustomNamingConventions());
+
+// After
+IReadOnlySchemaOptions capturedSchemaOptions;
+
+builder.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/v15/defining-a-schema/dynamic-schemas.md b/website/src/docs/hotchocolate/v15/defining-a-schema/dynamic-schemas.md
new file mode 100644
index 00000000000..925eb8bf799
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/dynamic-schemas.md
@@ -0,0 +1,359 @@
+---
+title: Dynamic Schemas
+---
+
+In the world of SaaS, one size rarely fits all. With the ever-changing requirements and the need for high flexibility, schemas in a web application often need to be dynamic. In the context of GraphQL, a dynamic schema allows you to adapt the data structure exposed by your API according to varying conditions, be it different tenant, changing data sources, or configuration.
+
+For instance, consider a Content Management System (CMS) where each tenant might require custom fields that are specific to their use case. Having a static GraphQL schema in such a scenario would mean that you need to anticipate all possible custom fields beforehand, which is not practical. A dynamic schema, on the other hand, allows you to add, remove, or modify the types and fields in your schema at runtime based on the specific needs of each tenant. Each tenant can have a different schema, and you can adapt the schema to the tenant's needs without having to redeploy your application.
+
+While creating dynamic schemas in GraphQL offers substantial flexibility, it also comes with its own set of complexities. This is where the `ITypeModule` interface in Hot Chocolate comes into play.
+
+# What is `ITypeModule`?
+
+`ITypeModule` is an interface introduced in Hot Chocolate that allows you to build a component that dynamically provides types to the schema building process.
+
+The `ITypeModule` interface consists of an event `TypesChanged` and a method `CreateTypesAsync`. Here is a brief overview of each:
+
+- `TypesChanged`: This event signals when types have changed and the current schema version needs to be phased out.
+
+- `CreateTypesAsync`: This method is called by the schema building process to create types for a new schema instance. It takes a descriptor context, which provides access to schema building services and conventions, and a cancellation token.
+
+When the underlying structure for a type module changes, for example, due to alterations in a database schema or updates in a JSON file defining types, the `TypesChanged` event can be triggered. This event tells Hot Chocolate to phase out the old schema and introduce a new one with the updated types from the module.
+
+In essence, `ITypeModule` takes care of the complexities associated with providing a dynamic schema with hot-reload functionality, allowing developers to focus on the core logic of their applications.
+
+In the following sections, we'll look at a couple of examples that demonstrate how to use `ITypeModule` to create dynamic schemas in different scenarios.
+
+# Example: Creating Types from a JSON File
+
+In this example, we'll explore how to create a dynamic schema from a JSON file. This scenario might be common if your application allows users to define custom types and fields through a UI, and these definitions are stored as JSON.
+
+Let's consider the following `ITypeModule` implementation:
+
+```csharp
+public class JsonTypeModule : ITypeModule
+{
+ private readonly string _file;
+
+ public JsonTypeModule(string file)
+ {
+ _file = file;
+ }
+
+ public event EventHandler? TypesChanged;
+
+ public async ValueTask> CreateTypesAsync(
+ IDescriptorContext context,
+ CancellationToken cancellationToken)
+ {
+ var types = new List();
+
+ await using var file = File.OpenRead(_file);
+ using var json = await JsonDocument.ParseAsync(file, cancellationToken: cancellationToken);
+
+ foreach (var type in json.RootElement.EnumerateArray())
+ {
+ var typeDefinition = new ObjectTypeDefinition(type.GetProperty("name").GetString()!);
+
+ foreach (var field in type.GetProperty("fields").EnumerateArray())
+ {
+ typeDefinition.Fields.Add(
+ new ObjectFieldDefinition(
+ field.GetString()!,
+ type: TypeReference.Parse("String!"),
+ pureResolver: ctx => "foo"));
+ }
+
+ types.Add(
+ type.GetProperty("extension").GetBoolean()
+ ? ObjectTypeExtension.CreateUnsafe(typeDefinition)
+ : ObjectType.CreateUnsafe(typeDefinition));
+ }
+
+ return types;
+ }
+}
+```
+
+In this implementation, `CreateTypesAsync` reads a JSON file, parses it, and creates types based on the content of the JSON. If any of these types are extensions, they are created as such. If the types or their structure change, you could fire the `TypesChanged` event to signal that a new schema needs to be generated.
+
+# Unsafe Type Creation
+
+When working with dynamic schemas and the `ITypeModule` interface, one of the practices you'll encounter is the use of the `CreateUnsafe` method to create types.
+The unsafe way to create types, as the name implies, bypasses some of the standard validation logic. This method is useful for advanced scenarios where you need more flexibility, such as when dynamically creating types based on runtime data.
+
+The `CreateUnsafe` method allows you to create types directly from a `TypeDefinition`.
+
+```csharp
+var typeDefinition = new ObjectTypeDefinition("DynamicType");
+// ... populate typeDefinition ...
+
+var dynamicType = ObjectType.CreateUnsafe(typeDefinition);
+```
+
+Using `CreateUnsafe` method for type creation can be a complex task as it involves operating directly on the type definition.
+This allows for a lot of flexibility, but it also requires a deeper understanding of the Hot Chocolate type system.
+
+Here are some examples of how you might use the `CreateUnsafe` method to create various types.
+
+> This is by no means an exhaustive list, but it should give you an idea of how to use this feature.
+
+## Creating an Object Type
+
+Let's say we want to create a new object type representing a `Product` in an e-commerce system.
+We would start by defining the `ObjectTypeDefinition`:
+
+```csharp
+var objectTypeDefinition = new ObjectTypeDefinition("Product")
+{
+ Description = "Represents a product in the e-commerce system",
+ RuntimeType = typeof(Dictionary)
+};
+```
+
+Next, we might want to add fields to this object type. For instance, a `Product` might have an `ID`, `Name`, and `Price`:
+
+```csharp
+var idFieldDefinition = new ObjectFieldDefinition(
+ "id",
+ "Unique identifier for the product",
+ TypeReference.Parse("ID!"),
+ pureResolver: context => context.Parent>()["id"]);
+
+var nameFieldDefinition = new ObjectFieldDefinition(
+ "name",
+ "Name of the product",
+ TypeReference.Parse("String!"),
+ pureResolver: context => context.Parent>()["name"]);
+
+var priceFieldDefinition = new ObjectFieldDefinition(
+ "price",
+ "Price of the product",
+ TypeReference.Parse("Float!"),
+ pureResolver: context => context.Parent>()["price"]);
+
+objectTypeDefinition.Fields.Add(idFieldDefinition);
+objectTypeDefinition.Fields.Add(nameFieldDefinition);
+objectTypeDefinition.Fields.Add(priceFieldDefinition);
+```
+
+Here, each resolver retrieves the corresponding value from the parent `Dictionary`.
+
+Next, let's add a field that calculates the price after applying a discount. This field would have an argument specifying the discount percentage:
+
+```csharp
+var discountArgument = new ArgumentDefinition(
+ "discount",
+ "Discount percentage to apply",
+ TypeReference.Parse("Float!"));
+
+var discountPriceField = new ObjectFieldDefinition(
+ "discountPrice",
+ "Price after discount",
+ TypeReference.Parse("Float!"),
+ pureResolver: context =>
+ {
+ var product = context.Parent>();
+ var discountPercentage = context.ArgumentValue("discount");
+ var originalPrice = (float) product["price"];
+ return originalPrice * (1 - discountPercentage / 100);
+ }
+)
+{
+ Arguments = { discountArgument }
+};
+
+objectTypeDefinition.Fields.Add(discountPriceField);
+```
+
+In this case, the `discountPrice` field takes a `discount` argument and uses it to calculate the discounted price. The resolver retrieves the original price from the parent `Dictionary`, applies the discount, and returns the discounted price.
+
+Finally, we create the `ObjectType` and register it:
+
+```csharp
+var productType = ObjectType.CreateUnsafe(objectTypeDefinition);
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ ... // other configuration
+ .AddType(productType);
+```
+
+Now our `Product` object type has fields `id`, `name`, `price`, and `discountPrice(discount: Float!)`. The `discountPrice` field takes a `discount` argument representing the discount percentage.
+
+## Resolver types
+
+A resolver in Hot Chocolate is a delegate that fetches the data for a specific field. There are two types of resolvers: _async Resolvers_ and _pure Resolvers_.
+
+1. **Async Resolvers**:
+
+ ```csharp
+ public delegate ValueTask FieldResolverDelegate(IResolverContext context);
+ ```
+
+ _Async Resolvers_ are are typically async and have access to a `IResolverContext`. They are usually used for fetching data from services or databases.
+
+2. **Pure Resolvers**:
+
+ ```csharp
+ public delegate object? PureFieldDelegate(IResolverContext context);
+ ```
+
+ _Pure Resolvers_ is used where no side-effects or async calls are needed. All your properties are turned into pure resolvers by Hot Chocolate.
+ The execution engine optimizes the execution of these resolvers (through inlining of the value completion) to make it significantly faster.
+
+The decision to use _async Resolvers_ or _pure Resolvers_ depends on your use case. If you need to perform asynchronous operations,or fetch data from services, you would use _async Resolvers_. If your resolver is simply retrieving data without any side effects, _pure Resolvers_ would be a more performant choice.
+
+Let's add a non-pure field resolver to our example. For instance, we can add a `reviews` field that fetches reviews for a product from an external service:
+
+```csharp
+var reviewsFieldDefinition = new ObjectFieldDefinition(
+ "reviews",
+ "Reviews for the product",
+ TypeReference.Parse("[Review!]"),
+ resolver: async context =>
+ {
+ var productId = context.Parent>()["id"];
+ var reviewsService = context.Service();
+ return await reviewsService.GetReviewsForProduct(productId);
+ });
+
+objectTypeDefinition.Fields.Add(reviewsFieldDefinition);
+```
+
+Here, `IReviewsService` could be an interface representing a service that fetches reviews. The `reviewsResolver` uses the `Service` method on the `IMiddlewareContext` to retrieve an instance of this service, then calls a method on this service to get the reviews. .
+
+This field resolver is a `FieldResolverDelegate` (i.e., a non-pure resolver) because it needs perform an asynchronous operation.
+
+The resulting schema is:
+
+```graphql
+"Represents a product in the e-commerce system"
+type Product {
+ "Unique identifier for the product"
+ id: ID!
+ "Name of the product"
+ name: String!
+ "Price of the product"
+ price: Float!
+ "Price after discount"
+ discountPrice("Discount percentage to apply" discount: Float!): Float!
+ "Reviews for the product"
+ reviews: String
+}
+```
+
+## Creating an Input Object Type
+
+Creating an Input Object Type is very similar to creating an Object Type. The major difference lies in the fact that Input Object Types are used in GraphQL mutations or as arguments in queries, whereas Object Types are used in GraphQL queries to define the shape of the returned data. Meaning you don't need to define resolvers for Input Object Types.
+
+An Input Object Type can be created by defining an `InputObjectTypeDefinition` and using the `InputObjectType.CreateUnsafe` method.
+
+Let's create an input object type representing a `ProductInput` which can be used to create or update a product:
+
+```csharp
+var inputObjectTypeDefinition = new InputObjectTypeDefinition("ProductInput")
+{
+ Description = "Represents product input for creating or updating a product",
+ RuntimeType = typeof(Dictionary)
+};
+
+var nameFieldDefinition = new InputFieldDefinition(
+ "name",
+ "Name of the product",
+ TypeReference.Parse("String!"));
+
+var priceFieldDefinition = new InputFieldDefinition(
+ "price",
+ "Price of the product",
+ TypeReference.Parse("Float!"));
+
+inputObjectTypeDefinition.Fields.Add(nameFieldDefinition);
+inputObjectTypeDefinition.Fields.Add(priceFieldDefinition);
+
+var productInputType = InputObjectType.CreateUnsafe(inputObjectTypeDefinition);
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ ... // other configuration
+ .AddType(productInputType);
+```
+
+As with Object Types, you can use the `CreateUnsafe` method to create complex input types based on runtime data.
+
+## Combining the Generated Types
+
+To create a GraphQL mutation, you need an `InputObjectType` to define the input of the mutation and an `ObjectType` to define the output. You can create a mutation by defining a `MutationTypeDefinition` and using the `MutationType.CreateUnsafe` method.
+
+Let's extend the previous examples to create a `createProduct` mutation using the `ProductInput` and `Product` types:
+
+```csharp
+var createProductMutationFieldDefinition = new ObjectFieldDefinition(
+ "createProduct",
+ "Creates a new product",
+ TypeReference.Parse("Product!"),
+ resolver: async context =>
+ {
+ var productInput = context.ArgumentValue>("input");
+ var productService = context.Service();
+ var newProduct = await productService.CreateProduct(productInput);
+ return newProduct;
+ }
+)
+{
+ Arguments =
+ {
+ new ArgumentDefinition(
+ "input",
+ "Input for creating the product",
+ TypeReference.Parse("ProductInput!"))
+ }
+};
+
+var mutationTypeDefinition = new ObjectTypeDefinition("Mutation")
+{
+ RuntimeType = typeof(object)
+};
+
+mutationTypeDefinition.Fields.Add(createProductMutationFieldDefinition);
+
+var mutationType = ObjectType.CreateUnsafe(mutationTypeDefinition);
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddMutationType(mutationType)
+ ... // other configuration
+ .AddType(productInputType)
+ .AddType(productType);
+```
+
+In this example, we first define a mutation field `createProduct` that takes a `ProductInput` argument and returns a `Product`. The resolver for this field uses a hypothetical `IProductService` to create a new product based on the input.
+
+We then define a `Mutation` type and add the `createProduct` field to it. Finally, we use the `CreateUnsafe` method to create the `Mutation` type and register it along with the `ProductInput` and `Product` types.
+
+With this setup, you can now use the `createProduct` mutation in your GraphQL API:
+
+```graphql
+mutation CreateProduct($input: ProductInput!) {
+ createProduct(input: $input) {
+ id
+ name
+ price
+ }
+}
+```
+
+With the variable:
+
+```json
+{
+ "input": {
+ "name": "New Product",
+ "price": 99.99
+ }
+}
+```
+
+This mutation will create a new product and return its details as a `Product` object.
+
+This way, you can use the generated `InputObjectType` and `ObjectType` together to create a complete GraphQL mutation. Similarly, you can combine other generated types to create the queries, subscriptions, and other parts of your GraphQL schema.
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/enums.md b/website/src/docs/hotchocolate/v15/defining-a-schema/enums.md
new file mode 100644
index 00000000000..70f6cfd04b4
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/enums.md
@@ -0,0 +1,369 @@
+---
+title: "Enums"
+---
+
+An Enum is a special kind of [scalar](/docs/hotchocolate/v15/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/v15/defining-a-schema/object-types#explicit-types) or we can register the type once globally:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddType();
+```
+
+With this configuration each `UserRole` CLR type we return from our resolvers would be assumed to be a `UserRoleType`.
+
+
+
+
+```csharp
+builder.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 implementation-first 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
+builder.Services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> Warning: 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 implementation-first 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_SNAKE_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/v15/defining-a-schema/extending-types.md b/website/src/docs/hotchocolate/v15/defining-a-schema/extending-types.md
new file mode 100644
index 00000000000..fcf7d7861b2
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/extending-types.md
@@ -0,0 +1,336 @@
+---
+title: "Extending Types"
+---
+
+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.
+
+
+
+> Warning: Type extensions do not produce the [extend type syntax that GraphQL offers](https://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
+ }
+}
+```
+
+```csharp
+builder.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
+ }
+}
+```
+
+```csharp
+builder.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
+ });
+ }
+}
+```
+
+```csharp
+builder.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
+ });
+ }
+}
+```
+
+```csharp
+builder.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
+{
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+```
+
+
+
+
+```csharp
+public class BookTypeExtensions : ObjectTypeExtension
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Ignore(f => f.AuthorId);
+ }
+}
+```
+
+```csharp
+builder.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
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddTypeExtension();
+```
+
+
+
+
+**This is currently not working ([#3776](https://github.com/ChilliCream/graphql-platform/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
+ });
+ }
+}
+```
+
+```csharp
+builder.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/v15/defining-a-schema/index.md b/website/src/docs/hotchocolate/v15/defining-a-schema/index.md
new file mode 100644
index 00000000000..883fe88c390
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/index.md
@@ -0,0 +1,87 @@
+---
+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/v15/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/v15/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/v15/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/v15/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/v15/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/v15/defining-a-schema/enums)
+- Interfaces represent a shared contract that other types can implement. [Learn more about interfaces](/docs/hotchocolate/v15/defining-a-schema/interfaces)
+- Unions represent a set of object types, without the need for a shared contract. [Learn more about unions](/docs/hotchocolate/v15/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/v15/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/v15/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/v15/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/v15/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/v15/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/v15/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/v15/defining-a-schema/documentation)
+
+[Learn more about versioning](/docs/hotchocolate/v15/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/v15/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/v15/defining-a-schema/input-object-types.md b/website/src/docs/hotchocolate/v15/defining-a-schema/input-object-types.md
new file mode 100644
index 00000000000..d9d36834879
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/input-object-types.md
@@ -0,0 +1,716 @@
+---
+title: "Input Object Types"
+---
+
+We already looked at [arguments](/docs/hotchocolate/v15/defining-a-schema/arguments), which allow us to use simple [scalars](/docs/hotchocolate/v15/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/v15/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
+}
+```
+
+# Defining an Input Type
+
+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/v15/defining-a-schema/object-types)
+
+
+
+
+```csharp
+public class BookInput
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+```
+
+```csharp
+builder.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);
+```
+
+# Default Values
+
+In GraphQL, default values can be assigned to arguments and input types. These values are automatically utilized if no other value is provided when a query or mutation is executed.
+
+Default values are specified in the GraphQL schema by appending `= value` to the argument or input type definition. For example: `field(value: Int = 10)` would give `value` a default of 10.
+
+Default values can be set for any input types, including scalars, enums, and input object types. They can also be used with list types and non-null types.
+
+Consider the following schema:
+
+```graphql
+type Query {
+ user(active: Boolean = true): [User]
+}
+
+input UserInput {
+ name: String
+ active: Boolean = true
+}
+```
+
+In the `user` query field, the `active` argument has a default value of `true`. Similarly, in the `UserInput` type, the `active` field defaults to `true`.
+
+In resolvers, arguments with default values are treated as optional. If the client does not provide a value, the resolver will receive the default value. This makes handling optional fields in your resolvers much easier.
+
+This means you can write the following query against the schema described before:
+
+```graphql
+query fetchUser {
+ user {
+ # active is not needed
+ name
+ }
+}
+```
+
+Default values also play a vital role in maintaining backward compatibility. When adding new fields to an input type or new arguments to a field, providing a default value ensures existing queries will still work.
+
+For instance, consider the situation where we want to extend the `user` field with another argument. As long as this new argument has a default value, it won't affect the functionality of the `fetchUser` query:
+
+```graphql
+type Query {
+ user(active: Boolean = true, role: String = "user"): User
+}
+```
+
+Despite the addition of the `role` argument, the `fetchUser` query can still be executed without supplying this new argument, as the `role` will default to `"user"`.
+
+## Specifying DefaultValues
+
+The `DefaultValueAttribute` or the `DefaultValue` method on the field descriptor, allow you to assign default values to your fields or arguments.
+
+Consider the following scenario where we have a `UserInput` type with different fields like `name`, `active`. By default, we would like `active` to be `true`.
+
+
+
+
+```csharp
+public class UserInput
+{
+ public string? Name { get; set; }
+ [DefaultValue(true)]
+ public bool IsActive { get; set; }
+}
+```
+
+This will produce the following schema:
+
+```sdl
+input UserInput {
+ name: String
+ active: Boolean! = true
+}
+```
+
+
+
+
+```csharp
+public class UserInput
+{
+ public string? Name { get; set; }
+ public bool IsActive { get; set; }
+}
+
+public class UserInputType : InputObjectType
+{
+ protected override void Configure(
+ IInputObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(t => t.IsActive).DefaultValue(true);
+ }
+}
+```
+
+This will produce the following schema:
+
+```sdl
+input UserInput {
+ name: String
+ active: Boolean! = true
+}
+```
+
+
+
+
+```sdl
+# In a schema-first approach, you would define it directly in your schema
+input UserInput {
+ name: String
+ active: Boolean! = true
+}
+```
+
+
+
+
+## Using GraphQL Syntax
+
+It is also possible to specify default values using GraphQL value syntax.
+This comes in handy when you want to set default values that are more than just simple scalars.
+Like for example objects or lists.
+
+Consider a scenario where we have a `UserProfileInput` type with a field `preferences`. The `preferences` field itself is an object containing various user preference settings.
+
+
+
+
+```csharp
+public class Preferences
+{
+ public bool Notifications { get; set; }
+ public string Theme { get; set; }
+}
+
+public class UserProfileInput
+{
+ public string? Name { get; set; }
+
+ [DefaultValueSyntax("{ notifications: true, theme: 'light' }")]
+ public Preferences? Preferences { get; set; }
+}
+```
+
+This will produce the following schema:
+
+```sdl
+input PreferencesInput {
+ notifications: Boolean
+ theme: String
+}
+
+input UserProfileInput {
+ name: String
+ preferences: PreferencesInput = { notifications: true, theme: "light" }
+}
+```
+
+
+
+
+```csharp
+public class Preferences
+{
+ public bool Notifications { get; set; }
+ public string Theme { get; set; }
+}
+
+public class UserProfileInput
+{
+ public string? Name { get; set; }
+ public Preferences? Preferences { get; set; }
+}
+
+public class UserProfileInputType : InputObjectType
+{
+ protected override void Configure(
+ IInputObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(t => t.Preferences)
+ .DefaultValueSyntax("{ notifications: true, theme: 'light' }");
+ }
+}
+```
+
+This will produce the following schema:
+
+```sdl
+input PreferencesInput {
+ notifications: Boolean
+ theme: String
+}
+
+input UserProfileInput {
+ name: String
+ preferences: PreferencesInput = { notifications: true, theme: "light" }
+}
+```
+
+
+
+
+```sdl
+# In a schema-first approach, you would define it directly in your schema
+input PreferencesInput {
+ notifications: Boolean
+ theme: String
+}
+
+input UserProfileInput {
+ name: String
+ preferences: PreferencesInput = { notifications: true, theme: "light" }
+}
+```
+
+
+
+
+In this example, if no value for `preferences` is provided when making a mutation, the system will automatically use the default value `{ notifications: true, theme: 'light' }`.
+
+# 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.
+
+> Warning: `OneOf` Input Objects is currently a draft feature to the GraphQL spec.
+
+
+
+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
+ }
+}
+```
+
+```csharp
+builder.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/v15/defining-a-schema/interfaces.md b/website/src/docs/hotchocolate/v15/defining-a-schema/interfaces.md
new file mode 100644
index 00000000000..2395378a4dc
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/interfaces.md
@@ -0,0 +1,608 @@
+---
+title: "Interfaces"
+---
+
+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
+ }
+}
+```
+
+```csharp
+builder.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; }
+}
+```
+
+```csharp
+builder.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));
+ }
+}
+```
+
+```csharp
+builder.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; }
+}
+```
+
+```csharp
+builder.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 implementation-first 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
+builder.Services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> Warning: 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 implementation-first 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
+ }
+}
+```
+
+```csharp
+builder.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));
+ }
+}
+```
+
+```csharp
+builder.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; }
+}
+```
+
+```csharp
+builder.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/v15/defining-a-schema/lists.md b/website/src/docs/hotchocolate/v15/defining-a-schema/lists.md
new file mode 100644
index 00000000000..c226272b334
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/lists.md
@@ -0,0 +1,103 @@
+---
+title: "Lists"
+---
+
+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 sub-selection 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/v15/defining-a-schema/mutations.md b/website/src/docs/hotchocolate/v15/defining-a-schema/mutations.md
new file mode 100644
index 00000000000..38d59e24709
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/mutations.md
@@ -0,0 +1,1030 @@
+---
+title: "Mutations"
+---
+
+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
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddMutationType();
+```
+
+
+
+
+```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));
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddMutationType();
+```
+
+
+
+
+```csharp
+public class Mutation
+{
+ public async Task AddBook(Book book)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Mutation {
+ addBook(input: BookInput): Book
+ }
+
+ input BookInput {
+ title: String
+ author: String
+ }
+
+ type Book {
+ title: String
+ author: String
+ }
+ ")
+ .BindRuntimeType();
+```
+
+
+
+
+> Warning: 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/v15/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/v15/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
+builder.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
+builder.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 laid 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 UserNameTakenError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new UserNameTakenError(ex.Username);
+ }
+
+ public static UserNameTakenError CreateErrorFrom(OtherException ex)
+ {
+ return new UserNameTakenError(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 UserNameTakenError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new UserNameTakenError(ex.Username);
+ }
+
+ public static UserNameTakenError CreateErrorFrom(OtherException ex)
+ {
+ return new UserNameTakenError(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 UserNameTakenError CreateErrorFrom(UserNameTakenException ex)
+ {
+ return new UserNameTakenError(ex.Username);
+ }
+
+ public static UserNameTakenError CreateErrorFrom(OtherException ex)
+ {
+ return new UserNameTakenError(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 UserNameTakenError CreateErrorFrom(DomainExceptionA ex)
+ {
+ return new UserNameTakenError();
+ }
+
+ public static UserNameTakenError CreateErrorFrom(DomainExceptionB ex)
+ {
+ return new UserNameTakenError();
+ }
+}
+
+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 MyCustomError CreateErrorFrom(DomainExceptionA ex)
+ {
+ return new MyCustomErrorA();
+ }
+
+ public MyCustomError CreateErrorFrom(DomainExceptionB ex)
+ {
+ return new MyCustomErrorB();
+ }
+}
+
+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
+builder.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; }
+}
+```
+
+```csharp
+builder.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>();
+ }
+}
+```
+
+```csharp
+builder.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/v15/defining-a-schema/non-null.md b/website/src/docs/hotchocolate/v15/defining-a-schema/non-null.md
new file mode 100644
index 00000000000..f799862064b
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/non-null.md
@@ -0,0 +1,151 @@
+---
+title: "Non-Null"
+---
+
+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/v15/defining-a-schema/object-types.md b/website/src/docs/hotchocolate/v15/defining-a-schema/object-types.md
new file mode 100644
index 00000000000..d8501e97888
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/object-types.md
@@ -0,0 +1,510 @@
+---
+title: "Object Types"
+---
+
+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 implementation-first 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
+builder.Services
+ .AddGraphQLServer()
+ .AddType();
+```
+
+With this configuration every `Author` CLR type we return from our resolvers 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; }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Author {
+ name: String
+ }
+ ")
+ .BindRuntimeType();
+```
+
+
+
+
+# Binding behavior
+
+In the implementation-first 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
+builder.Services
+ .AddGraphQLServer()
+ .ModifyOptions(options =>
+ {
+ options.DefaultBindingBehavior = BindingBehavior.Explicit;
+ });
+```
+
+> Warning: 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 implementation-first 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 implementation-first 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; }
+}
+```
+
+```csharp
+builder.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/v15/fetching-data/resolvers)
+
+# Generics
+
+> Note: Read about [interfaces](/docs/hotchocolate/v15/defining-a-schema/interfaces) and [unions](/docs/hotchocolate/v15/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
+ where T : class, IOutputType
+{
+ protected override void Configure(
+ IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Name(dependency => dependency.Name + "Response")
+ .DependsOn();
+
+ descriptor.Field(f => f.Status);
+
+ descriptor
+ .Field(f => f.Payload)
+ .Type();
+ }
+}
+```
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/queries.md b/website/src/docs/hotchocolate/v15/defining-a-schema/queries.md
new file mode 100644
index 00000000000..e1e1339ca6b
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/queries.md
@@ -0,0 +1,135 @@
+---
+title: "Queries"
+---
+
+The query type in GraphQL represents a read-only view of all of our entities and ways to retrieve them. A query type is required for every GraphQL server.
+
+```sdl
+type Query {
+ books: [Book!]!
+ author(id: Int!): Author
+}
+```
+
+Clients can query one or more fields through the query type.
+
+```graphql
+query {
+ books {
+ title
+ author
+ }
+ author(id: 1) {
+ name
+ }
+}
+```
+
+Queries are expected to be side-effect free and are therefore parallelized by the execution engine.
+
+# Usage
+
+A query type can be defined like the following.
+
+
+
+
+```csharp
+public class Query
+{
+ public Book GetBook()
+ {
+ return new Book { Title = "C# in depth", Author = "Jon Skeet" };
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+public class Query
+{
+ public Book GetBook()
+ {
+ return new Book { Title = "C# in depth", Author = "Jon Skeet" };
+ }
+}
+
+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();
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+public class Query
+{
+ public Book GetBook()
+ {
+ return new Book { Title = "C# in depth", Author = "Jon Skeet" };
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ book: Book
+ }
+
+ type Book {
+ title: String
+ author: String
+ }
+ ")
+ .BindRuntimeType()
+ .BindRuntimeType();
+```
+
+
+
+
+> Warning: Only **one** query type can be registered using `AddQueryType()`. If we want to split up our query type into multiple classes, we can do so using type extensions.
+>
+> [Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+A query type is just a regular object type, so everything that applies to an object type also applies to the query type (this is true for all root types).
+
+[Learn more about object types](/docs/hotchocolate/v15/defining-a-schema/object-types)
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/relay.md b/website/src/docs/hotchocolate/v15/defining-a-schema/relay.md
new file mode 100644
index 00000000000..350f0ea8318
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/relay.md
@@ -0,0 +1,605 @@
+---
+title: "Relay"
+---
+
+> Note: Even though they originated in Relay, the design principles described in this document are not exclusive to Relay. They lead to an overall better schema design, which is why we recommend them to **all** users of Hot Chocolate.
+
+[Relay](https://relay.dev) is a JavaScript framework for building data-driven React applications with GraphQL, which is developed and used by _Facebook_.
+
+As part of a specification Relay proposes some schema design principles for GraphQL servers in order to more efficiently fetch, refetch and cache entities on the client. In order to get the most performance out of Relay our GraphQL server needs to abide by these principles.
+
+[Learn more about the Relay GraphQL Server Specification](https://relay.dev/docs/guides/graphql-server-specification)
+
+
+
+# Global identifiers
+
+If an output type contains an `id: ID!` field, [Relay](https://relay.dev) and other GraphQL clients will consider this the unique identifier of the entity and might use it to construct a flat cache. This can be problematic, since we could have the same identifier for two of our types. When using a database for example, a `Foo` and `Bar` entity could both contain a row with the identifier `1` in their respective tables.
+
+We could try and enforce unique identifiers for our Ids. Still, as soon as we introduce another data source to our schema, we might be facing identifier collisions between entities of our various data sources.
+
+Fortunately there is an easier, more integrated way to go about solving this problem in Hot Chocolate: Global identifiers.
+
+With Global Identifiers, Hot Chocolate adds a middleware that automatically serializes our identifiers to be unique within the schema. The concern of globally unique identifiers is therefore kept separate from our business domain and we can continue using the "real" identifiers within our business code, without worrying about uniqueness for a client.
+
+## Usage in Output Types
+
+Id fields can be opted in to the global identifier behavior using the `ID` middleware.
+
+Hot Chocolate automatically combines the value of fields annotated as `ID` with another value to form a global identifier. Per default, this additional value is the name of the type the Id belongs to. Since type names are unique within a schema, this ensures that we are returning a unique Id within the schema. If our GraphQL server serves multiple schemas, the schema name is also included in this combined Id. The resulting Id is then Base64 encoded to make it opaque.
+
+
+
+
+```csharp
+public class Product
+{
+ [ID]
+ public int Id { get; set; }
+}
+```
+
+If no arguments are passed to the `[ID]` attribute, it will use the name of the output type, in this case `Product`, to serialize the Id.
+
+The `[ID]` attribute can be used on primary key fields and on fields that act as foreign keys. For these, we have to specify the name of the type they are referencing manually. In the below example, a type named `Foo` is being referenced using its Id.
+
+```csharp
+[ID("Foo")]
+public int FooId { get; set; }
+```
+
+
+
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(f => f.Id).ID();
+ }
+}
+```
+
+If no arguments are passed to `ID()`, it will use the name of the output type, in this case `Product`, to serialize the Id.
+
+The `ID()` can not only be used on primary key fields but also on fields that act as foreign keys. For these, we have to specify the name of the type they are referencing manually. In the below example, a type named `Foo` is being referenced using its Id.
+
+```csharp
+descriptor.Field(f => f.FooId).ID("Foo");
+```
+
+
+
+
+The approach of either implementation-first or code-first can be used in conjunction with schema-first.
+
+
+
+
+The type of fields specified as `ID` is also automatically rewritten to the ID scalar.
+
+[Learn more about the ID scalar](/docs/hotchocolate/v15/defining-a-schema/scalars#id)
+
+## Usage in Input Types
+
+If our `Product` output type returns a serialized Id, all arguments and fields on input object types, accepting a `Product` Id, need to be able to interpret the serialized Id.
+Therefore we also need to define them as `ID`, in order to deserialize the serialized Id to the actual Id.
+
+
+
+
+```csharp
+public class Query
+{
+ public Product GetProduct([ID] int id)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+In input object types we can use the `[ID]` attribute on specific fields.
+
+```csharp
+public class ProductInput
+{
+ [ID]
+ public int ProductId { get; set; }
+}
+```
+
+Per default all serialized Ids are accepted. If we want to only accept Ids that have been serialized for the `Product` output type, we can specify the type name as argument to the `[ID]` attribute.
+
+```csharp
+public Product GetProduct([ID(nameof(Product))] int id)
+```
+
+This will result in an error if an Id, serialized using a different type name than `Product`, is used as input.
+
+
+
+
+```csharp
+descriptor
+ .Field("product")
+ .Argument("id", a => a.Type>().ID())
+ .Type()
+ .Resolve(context =>
+ {
+ var id = context.ArgumentValue("id");
+
+ // Omitted code for brevity
+ });
+```
+
+> Note: `ID()` can only be used on fields and arguments with a concrete type. Otherwise type modifiers like non-null or list can not be correctly rewritten.
+
+In input object types we can use `ID()` on specific fields.
+
+```csharp
+descriptor
+ .Field("id")
+ .Type>()
+ .ID();
+```
+
+Per default all serialized Ids are accepted. If we want to only accept Ids that have been serialized for the `Product` output type, we can specify the type name as argument to `ID()`.
+
+```csharp
+.Argument("id", a => a.Type>().ID(nameof(Product)))
+```
+
+This will result in an error if an Id, serialized using a different type name than `Product`, is used as input.
+
+
+
+
+The approach of either implementation-first or code-first can be used in conjunction with schema-first.
+
+
+
+
+## Id Serializer
+
+Unique (or global) Ids are generated using the `IIdSerializer`. We can access it like any other service and use it to serialize or deserialize global Ids ourselves.
+
+```csharp
+public class Query
+{
+ public string Example(IIdSerializer serializer)
+ {
+ string serializedId = serializer.Serialize(null, "Product", "123");
+
+ IdValue deserializedIdValue = serializer.Deserialize(serializedId);
+ object deserializedId = deserializedIdValue.Value;
+
+ // Omitted code for brevity
+ }
+}
+```
+
+The `Serialize()` method takes the schema name as a first argument, followed by the type name and lastly the actual Id.
+
+[Learn more about accessing services](/docs/hotchocolate/v15/fetching-data/resolvers#injecting-services)
+
+# Complex Ids
+
+In certain situations, you may need to use complex identifiers for your data models, rather than simple integers or strings. HotChocolate provides support for complex IDs by allowing you to define custom ID types, which can be used in your GraphQL schema.
+
+## Defining Complex ID
+
+To define a complex ID, you need to create a new class or struct that will represent the complex ID, and use the `[ID]` attribute in the corresponding data model class. In this example, we will create a complex ID for a `Product` class.
+
+```csharp
+public class Product
+{
+ [ID] // Define the ID on the type
+ public ProductId Id { get; set; }
+}
+```
+
+### Using Type Extensions for Complex ID
+
+If your `Product` model does not have an ID field, but you still want to use a complex ID for GraphQL queries, you can use a type extension.
+
+A type extension allows you to add fields to a type that are only available within the GraphQL schema, without modifying the actual data model.
+Here's how you can define the type extension:
+
+```csharp
+[ExtendObjectType(typeof(Product))]
+public class ProductExtensions
+{
+ // Define a method that will be used to compute the complex ID
+ [ID]
+ public ProductId GetId([Parent] Product product)
+ => new ProductId(product.SKU, product.BatchNumber);
+}
+```
+
+This approach allows you to use complex IDs in your GraphQL schema without needing to modify your data models. It's particularly useful when working with databases that use **compound primary keys**, as it allows you to represent these keys as complex IDs in your GraphQL schema.
+
+## Creating Complex ID Structs
+
+Here's how you can define the `ProductId` struct:
+
+```csharp
+public readonly record struct ProductId(string SKU, int BatchNumber)
+{
+ // Override ToString to provide a string representation for the complex ID
+ public override string ToString() => $"{SKU}:{BatchNumber}";
+
+ // Create a Parse method that converts a string representation back to the complex ID
+ public static ProductId Parse(string value)
+ {
+ var parts = value.Split(':');
+ return new ProductId(parts[0], int.Parse(parts[1]));
+ }
+}
+```
+
+This struct has a string `SKU` and an integer `BatchNumber` property, and can be converted to and from a string for easy usage in GraphQL queries.
+
+## Configuring Type Converters
+
+To integrate the `ProductId` struct into HotChocolate's type system, you need to define type converters. These converters enable HotChocolate to automatically transform between the `ProductId` struct and a string representation.
+
+```csharp
+builder.Services.AddGraphQLServer()
+ .AddQueryType()
+ // Add a type converter from string to your complex ID type
+ .AddTypeConverter(ProductId.Parse)
+ // Add a type converter back to string
+ .AddTypeConverter(x => x.ToString())
+ // Enable global object identification
+ .AddGlobalObjectIdentification();
+```
+
+With these converters, you can now use `ProductId` as an ID in your GraphQL schema. When you receive a `ProductId` ID in a request, HotChocolate will automatically use the `ProductId.Parse` method to convert it into a `ProductId` object. Likewise, when returning a `ProductId` object in a response, HotChocolate will use the `ToString` method to convert it back into a string.
+
+# Global Object Identification
+
+Global Object Identification, as the name suggests, is about being able to uniquely identify an object within our schema. Moreover, it allows consumers of our schema to refetch an object in a standardized way. This capability allows client applications, such as [Relay](https://relay.dev), to automatically refetch types.
+
+To identify types that can be re-fetched, a new `Node` interface type is introduced.
+
+```sdl
+interface Node {
+ id: ID!
+}
+```
+
+Implementing this type signals to client applications, that the implementing type can be re-fetched. Implementing it also enforces the existence of an `id` field, a unique identifier, needed for the refetch operation.
+
+To refetch the types implementing the `Node` interface, a new `node` field is added to the query.
+
+```sdl
+type Query {
+ node(id: ID!): Node
+}
+```
+
+While it is not part of the specification, it is recommended to add the ability for plural fetches. That's why Hot Chocolate adds a `nodes` field allowing us to refetch multiple objects in one round trip.
+
+```sdl
+type Query {
+ node(id: ID!): Node
+ nodes(ids: [ID!]!): [Node]!
+}
+```
+
+## Usage
+
+In Hot Chocolate we can enable Global Object Identification, by calling `AddGlobalObjectIdentification()` on the `IRequestExecutorBuilder`.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddGlobalObjectIdentification()
+ .AddQueryType();
+```
+
+This registers the `Node` interface type and adds the `node(id: ID!): Node` and the `nodes(ids: [ID!]!): [Node]!` field to our query type. At least one type in our schema needs to implement the `Node` interface or an exception is raised.
+
+> Warning: Using `AddGlobalObjectIdentification()` in two upstream stitched services does currently not work out of the box.
+
+Next we need to extend our object types with the `Global Object Identification` functionality. Therefore 3 criteria need to be fulfilled:
+
+1. The type needs to implement the `Node` interface.
+2. On the type an `id` field needs to be present to properly implement the contract of the `Node` interface.
+3. A method responsible for refetching an object based on its `id` needs to be defined.
+
+
+
+
+To declare an object type as a re-fetchable, we need to annotate it using the `[Node]` attribute. This in turn causes the type to implement the `Node` interface and if present automatically turns the `id` field into a [global identifier](#global-identifiers).
+
+There also needs to be a method, a _node resolver_, responsible for the actual refetching of the object. Assuming our class is called `Product`, Hot Chocolate looks for a static method, with one of the following names:
+
+- `Get`
+- `GetAsync`
+- `GetProduct`
+- `GetProductAsync`
+
+The method is expected to have a return type of either `Product` or `Task`. Furthermore the first argument of this method is expected to be of the same type as the `Id` property. At runtime Hot Chocolate will invoke this method with the `id` of the object that should be re-fetched. Special types, such as services, can be injected as arguments as well.
+
+```csharp
+[Node]
+public class Product
+{
+ public string Id { get; set; }
+
+ public static async Task Get(string id, ProductService service)
+ {
+ Product product = await service.GetByIdAsync(id);
+
+ return product;
+ }
+}
+```
+
+If we need to influence the global identifier generation, we can annotate the `Id` property manually.
+
+```csharp
+[ID("Example")]
+public string Id { get; set; }
+```
+
+If the `Id` property of our class is not called `id`, we can either [rename it](/docs/hotchocolate/v15/defining-a-schema/object-types#naming) or specify the name of the property that should be the `id` field through the `[Node]` attribute. Hot Chocolate will then automatically rename this property to `id` in the schema to properly implement the contract of the `Node` interface.
+
+```csharp
+[Node(IdField = nameof(ProductId))]
+public class Product
+{
+ public string ProductId { get; set; }
+
+ // Omitted code for brevity
+}
+```
+
+If our _node resolver_ method doesn't follow the naming conventions laid out above, we can annotate it using the `[NodeResolver]` attribute to let Hot Chocolate know that this should be the method used for refetching the object.
+
+```csharp
+[NodeResolver]
+public static Product OtherMethod(string id)
+{
+ // Omitted code for brevity
+}
+```
+
+If we want to resolve the object using another class, we can reference the class/method like the following.
+
+```csharp
+[Node(NodeResolverType = typeof(ProductNodeResolver),
+ NodeResolver = nameof(ProductNodeResolver.MethodName))]
+public class Product
+{
+ public string ProductId { get; set; }
+}
+
+public class ProductNodeResolver
+{
+ public static Product MethodName(string id)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+When placing the `Node` functionality in an extension type, it is important to keep in mind that the `[Node]` attribute needs to be defined on the class extending the original type.
+
+```csharp
+[Node]
+[ExtendObjectType(typeof(Product))]
+public class ProductExtensions
+{
+ public Product GetProductAsync(string id)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+[Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+
+
+
+In the code-first approach, we have multiple APIs on the `IObjectTypeDescriptor` to fulfill these criteria:
+
+- `ImplementsNode`: Implements the `Node` interface.
+- `IdField`: Selects the property that represents the unique identifier of the object.
+- `ResolveNode` / `ResolveNodeWith`: Method that re-fetches the object by its Id, also called the _node resolver_. If these methods are chained after `IdField`, they automatically infer the correct type of the `id` argument.
+
+```csharp
+public class Product
+{
+ public string Id { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .ImplementsNode()
+ .IdField(f => f.Id)
+ .ResolveNode(async (context, id) =>
+ {
+ Product product =
+ await context.Service().GetByIdAsync(id);
+
+ return product;
+ });
+ }
+}
+```
+
+> Warning: When using middleware such as `UseDbContext` it needs to be chained after the `ResolveNode` call. The order of middleware still matters.
+
+If the `Id` property of our class is not called `id`, we can either [rename it](/docs/hotchocolate/v15/defining-a-schema/object-types#naming) or specify it through the `IdField` method on the `IObjectTypeDescriptor`. Hot Chocolate will then automatically rename this property to `id` in the schema to properly implement the contract of the `Node` interface.
+
+```csharp
+public class Product
+{
+ public string ProductId { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .ImplementsNode()
+ .IdField(f => f.ProductId)
+ .ResolveNode((context, id) =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+In case we want to resolve the object using another class, we can do so using `ResolveNodeWith`.
+
+```csharp
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .ImplementsNode()
+ .IdField(f => f.ProductId)
+ .ResolveNodeWith(r =>
+ r.GetProductAsync(default));
+ }
+}
+
+public class ProductNodeResolver
+{
+ public async Task GetProductAsync(string id)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+The approach of either implementation-first or code-first can be used in conjunction with schema-first.
+
+
+
+
+Since node resolvers resolve entities by their Id, they are the perfect place to start utilizing DataLoaders.
+
+[Learn more about DataLoaders](/docs/hotchocolate/v15/fetching-data/dataloader)
+
+# Connections
+
+_Connections_ are a standardized way to expose pagination capabilities.
+
+```sdl
+type Query {
+ users(first: Int after: String last: Int before: String): UsersConnection
+}
+
+type UsersConnection {
+ pageInfo: PageInfo!
+ edges: [UsersEdge!]
+ nodes: [User!]
+}
+
+type UsersEdge {
+ cursor: String!
+ node: User!
+}
+
+type PageInfo {
+ hasNextPage: Boolean!
+ hasPreviousPage: Boolean!
+ startCursor: String
+ endCursor: String
+}
+```
+
+[Learn more about Connections](/docs/hotchocolate/v15/fetching-data/pagination#connections)
+
+# Query field in Mutation payloads
+
+It's a common best practice to return a payload type from mutations containing the affected entity as a field.
+
+```sdl
+type Mutation {
+ likePost(id: ID!): LikePostPayload
+}
+
+type LikePostPayload {
+ post: Post
+}
+```
+
+This allows us to immediately process the affected entity in the client application responsible for the mutation.
+
+Sometimes a mutation might affect other parts of our application as well. Maybe the `likePost` mutation needs to update an Activity Feed.
+
+For this scenario, we can expose a `query` field on our payload type to allow the client application to fetch everything it needs to update its state in one round trip.
+
+```sdl
+type LikePostPayload {
+ post: Post
+ query: Query
+}
+```
+
+A resulting mutation request could look like the following.
+
+```graphql
+mutation {
+ likePost(id: 1) {
+ post {
+ id
+ content
+ likes
+ }
+ query {
+ ...ActivityFeed_Fragment
+ }
+ }
+}
+```
+
+## Usage
+
+Hot Chocolate allows us to automatically add this `query` field to all of our mutation payload types:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryFieldToMutationPayloads();
+```
+
+By default, this will add a field of type `Query` called `query` to each top-level mutation field type, whose name ends in `Payload`.
+
+Of course these defaults can be tweaked:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryFieldToMutationPayloads(options =>
+ {
+ options.QueryFieldName = "rootQuery";
+ options.MutationPayloadPredicate =
+ (type) => type.Name.Value.EndsWith("Result");
+ });
+```
+
+This would add a field of type `Query` with the name of `rootQuery` to each top-level mutation field type, whose name ends in `Result`.
+
+> Warning: This feature currently doesn't work on a stitching gateway, however this will be addressed in a future release focused on stitching. It's tracked as [#3158](https://github.com/ChilliCream/graphql-platform/issues/3158).
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/scalars.md b/website/src/docs/hotchocolate/v15/defining-a-schema/scalars.md
new file mode 100644
index 00000000000..8429cdd951f
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/scalars.md
@@ -0,0 +1,635 @@
+---
+title: "Scalars"
+---
+
+Scalar types are the primitives of our schema and can hold a specific type of data. They are leaf types, meaning we cannot use e.g. `{ fieldName }` to further drill down into the type. The main purpose of a scalar is to define how a value is serialized and deserialized.
+
+Besides basic scalars like `String` and `Int`, we can also create custom scalars like `CreditCardNumber` or `SocialSecurityNumber`. These custom scalars can greatly enhance the expressiveness of our schema and help new developers to get a grasp of our API.
+
+# GraphQL scalars
+
+The GraphQL specification defines the following scalars.
+
+## String
+
+```sdl
+type Product {
+ description: String;
+}
+```
+
+This scalar represents an UTF-8 character sequence.
+
+It is automatically inferred from the usage of the .NET [string type](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/reference-types#the-string-type).
+
+## Boolean
+
+```sdl
+type Product {
+ purchasable: Boolean;
+}
+```
+
+This scalar represents a Boolean value, which can be either `true` or `false`.
+
+It is automatically inferred from the usage of the .NET [bool type](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool).
+
+## Int
+
+```sdl
+type Product {
+ quantity: Int;
+}
+```
+
+This scalar represents a signed 32-bit numeric non-fractional value.
+
+It is automatically inferred from the usage of the .NET [int type](https://docs.microsoft.com/dotnet/api/system.int32).
+
+## Float
+
+```sdl
+type Product {
+ price: Float;
+}
+```
+
+This scalar represents double-precision fractional values, as specified by IEEE 754.
+
+It is automatically inferred from the usage of the .NET [float](https://docs.microsoft.com/dotnet/api/system.single) or [double type](https://docs.microsoft.com/dotnet/api/system.double).
+
+> Note: We introduced a separate `Decimal` scalar to handle `decimal` values.
+
+## ID
+
+```sdl
+type Product {
+ id: ID!;
+}
+```
+
+This scalar is used to facilitate technology-specific Ids, like `int`, `string` or `Guid`.
+
+It is **not** automatically inferred and the `IdType` needs to be [explicitly specified](/docs/hotchocolate/v15/defining-a-schema/object-types#explicit-types).
+
+`ID` values are always represented as a [String](#string) in client-server communication, but can be coerced to their expected type on the server.
+
+
+
+
+```csharp
+public class Product
+{
+ [GraphQLType(typeof(IdType))]
+ public int Id { get; set; }
+}
+
+public class Query
+{
+ public Product GetProduct([GraphQLType(typeof(IdType))] int id)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public class Product
+{
+ public int Id { get; set; }
+}
+
+public class ProductType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name("Product");
+
+ descriptor.Field(f => f.Id).Type();
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Name(OperationTypeNames.Query);
+
+ descriptor
+ .Field("product")
+ .Argument("id", a => a.Type())
+ .Type()
+ .Resolve(context =>
+ {
+ var id = context.ArgumentValue("id");
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+```csharp
+public class Product
+{
+ public int Id { get; set; }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ product(id: ID): Product
+ }
+
+ type Product {
+ id: ID
+ }
+ ")
+ .BindRuntimeType()
+ .AddResolver("Query", "product", context =>
+ {
+ var id = context.ArgumentValue("id");
+
+ // Omitted code for brevity
+ });
+```
+
+
+
+
+Notice how our code uses `int` for the `Id`, but in a request / response it would be serialized as a `string`. This allows us to switch the CLR type of our `Id`, without affecting the schema and our clients.
+
+# GraphQL Community Scalars
+
+The website hosts specifications for GraphQL scalars defined by the community. The community scalars use the `@specifiedBy` directive to point to the spec that is implemented.
+
+```sdl
+scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
+```
+
+## DateTime Type
+
+A custom GraphQL scalar which represents an exact point in time. This point in time is specified by having an offset to UTC and does not use time zone.
+
+The DateTime scalar is based on [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339).
+
+```sdl
+scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time/")
+```
+
+> Note: The Hot Chocolate implementation diverges slightly from the DateTime Scalar specification, and allows fractional seconds of 0-7 digits, as opposed to exactly 3.
+
+
+
+# .NET Scalars
+
+In addition to the scalars defined by the specification, Hot Chocolate also supports the following set of scalar types:
+
+| Type | Description |
+| ----------- | ------------------------------------------------------------ |
+| `Byte` | Byte |
+| `ByteArray` | Base64 encoded array of bytes |
+| `Short` | Signed 16-bit numeric non-fractional value |
+| `Long` | Signed 64-bit numeric non-fractional value |
+| `Decimal` | .NET Floating Point Type |
+| `Url` | Url |
+| `Date` | ISO-8601 date |
+| `TimeSpan` | ISO-8601 duration |
+| `Uuid` | GUID |
+| `Any` | This type can be anything, string, int, list or object, etc. |
+
+## Uuid Type
+
+The `Uuid` scalar supports the following serialization formats.
+
+| Specifier | Format |
+| ----------- | -------------------------------------------------------------------- |
+| N | 00000000000000000000000000000000 |
+| D (default) | 00000000-0000-0000-0000-000000000000 |
+| B | {00000000-0000-0000-0000-000000000000} |
+| P | (00000000-0000-0000-0000-000000000000) |
+| X | {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} |
+
+The `UuidType` will always return the value in the specified format. In case it is used as an input type, it will first try to parse the result in the specified format. If the parsing does not succeed, it will try to parse the value in other formats.
+
+To change the default format we have to register the `UuidType` with the specifier on the schema:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddType(new UuidType('D'));
+```
+
+## Any Type
+
+The `Any` scalar is a special type that can be compared to `object` in C#.
+`Any` allows us to specify any literal or return any output type.
+
+Consider the following type:
+
+```sdl
+type Query {
+ foo(bar: Any): String
+}
+```
+
+Since our field `foo` specifies an argument `bar` of type `Any` all of the following queries would be valid:
+
+```graphql
+{
+ a: foo(bar: 1)
+ b: foo(bar: [1, 2, 3, 4, 5])
+ a: foo(bar: "abcdef")
+ a: foo(bar: true)
+ a: foo(bar: { a: "foo", b: { c: 1 } })
+ a: foo(bar: [{ a: "foo", b: { c: 1 } }, { a: "foo", b: { c: 1 } }])
+}
+```
+
+The same goes for the output side. `Any` can return a structure of data although it is a scalar type.
+
+If we want to access the data we can either fetch data as an object or you can ask the context to provide it as a specific object.
+
+```csharp
+object foo = context.ArgumentValue("bar");
+Foo foo = context.ArgumentValue("bar");
+```
+
+We can also ask the context which kind the current argument is:
+
+```csharp
+ValueKind kind = context.ArgumentKind("bar");
+```
+
+The value kind will tell us by which kind of literal the argument is represented.
+
+> An integer literal can still contain a long value and a float literal could be a decimal but it also could just be a float.
+
+```csharp
+public enum ValueKind
+{
+ String,
+ Integer,
+ Float,
+ Boolean,
+ Enum,
+ Object,
+ Null
+}
+```
+
+If we want to access an object dynamically without serializing it to a strongly typed model we can get it as `IReadOnlyDictionary` or as `ObjectValueNode`.
+
+Lists can be accessed generically by getting them as `IReadOnlyList` or as `ListValueNode`.
+
+# Additional Scalars
+
+We also offer a separate package with scalars for more specific use cases.
+
+To use these scalars we have to add the `HotChocolate.Types.Scalars` package.
+
+
+
+**Available Scalars:**
+
+| Type | Description |
+| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| EmailAddress | Email address, represented as UTF-8 character sequences, as defined in [RFC5322](https://tools.ietf.org/html/rfc5322) |
+| HexColor | HEX color code |
+| Hsl | CSS HSL color as defined [here][1] |
+| Hsla | CSS HSLA color as defined [here][1] |
+| IPv4 | IPv4 address as defined [here](https://en.wikipedia.org/wiki/IPv4) |
+| IPv6 | IPv6 address as defined in [RFC8064](https://tools.ietf.org/html/rfc8064) |
+| Isbn | ISBN-10 or ISBN-13 number as defined [here](https://en.wikipedia.org/wiki/International_Standard_Book_Number) |
+| Latitude | Decimal degrees latitude number |
+| Longitude | Decimal degrees longitude number |
+| LocalCurrency | Currency string |
+| LocalDate | ISO date string, represented as UTF-8 character sequences yyyy-mm-dd, as defined in [RFC3339][2] |
+| LocalTime | Local time string (i.e., with no associated timezone) in 24-hr `HH:mm:ss` |
+| MacAddress | IEEE 802 48-bit (MAC-48/EUI-48) and 64-bit (EUI-64) Mac addresses, represented as UTF-8 character sequences, as defined in [RFC7042][3] and [RFC7043][4] |
+| NegativeFloat | Double‐precision fractional value less than 0 |
+| NegativeInt | Signed 32-bit numeric non-fractional with a maximum of -1 |
+| NonEmptyString | Non empty textual data, represented as UTF‐8 character sequences with at least one character |
+| NonNegativeFloat | Double‐precision fractional value greater than or equal to 0 |
+| NonNegativeInt | Unsigned 32-bit numeric non-fractional value greater than or equal to 0 |
+| NonPositiveFloat | Double‐precision fractional value less than or equal to 0 |
+| NonPositiveInt | Signed 32-bit numeric non-fractional value less than or equal to 0 |
+| PhoneNumber | A value that conforms to the standard E.164 format as defined [here](https://en.wikipedia.org/wiki/E.164) |
+| PositiveInt | Signed 32‐bit numeric non‐fractional value of at least the value 1 |
+| PostalCode | Postal code |
+| Port | TCP port within the range of 0 to 65535 |
+| Rgb | CSS RGB color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color_value#rgb_colors) |
+| Rgba | CSS RGBA color as defined [here](https://developer.mozilla.org/docs/Web/CSS/color_value#rgb_colors) |
+| SignedByte | Signed 8-bit numeric non‐fractional value greater than or equal to -127 and smaller than or equal to 128. |
+| UnsignedInt | Unsigned 32‐bit numeric non‐fractional value greater than or equal to 0 |
+| UnsignedLong | Unsigned 64‐bit numeric non‐fractional value greater than or equal to 0 |
+| UnsignedShort | Unsigned 16‐bit numeric non‐fractional value greater than or equal to 0 and smaller or equal to 65535. |
+| UtcOffset | A value of format `±hh:mm` |
+
+[1]: https://developer.mozilla.org/docs/Web/CSS/color_value#hsl_colors
+[2]: https://tools.ietf.org/html/rfc3339
+[3]: https://tools.ietf.org/html/rfc7042#page-19
+[4]: https://tools.ietf.org/html/rfc7043
+
+Most of these scalars are built on top of native .NET types. An Email Address for example is represented as a `string`, but just returning a `string` from our resolver would result in Hot Chocolate interpreting it as a `StringType`. We need to explicitly specify that the returned type (`string`) should be treated as an `EmailAddressType`.
+
+```csharp
+[GraphQLType(typeof(EmailAddressType))]
+public string GetEmail() => "test@example.com";
+```
+
+[Learn more about explicitly specifying GraphQL types](/docs/hotchocolate/v15/defining-a-schema/object-types#explicit-types)
+
+## NodaTime
+
+We also offer a package specifically for [NodaTime](https://github.com/nodatime/nodatime).
+
+It can be installed like the following.
+
+
+
+**Available Scalars:**
+
+| Type | Description | Example |
+| -------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- |
+| DateTimeZone | A [NodaTime DateTimeZone](https://nodatime.org/TimeZones) | `"Europe/Rome"` |
+| Duration | A [NodaTime Duration](https://nodatime.org/3.0.x/userguide/duration-patterns) | `"-123:07:53:10.019"` |
+| Instant | A [NodaTime Instant](https://nodatime.org/3.0.x/userguide/instant-patterns) | `"2020-02-20T17:42:59Z"` |
+| IsoDayOfWeek | A [NodaTime IsoDayOfWeek](https://nodatime.org/3.0.x/api/NodaTime.IsoDayOfWeek.html) | `7` |
+| LocalDate | A [NodaTime LocalDate](https://nodatime.org/3.0.x/userguide/localdate-patterns) | `"2020-12-25"` |
+| LocalDateTime | A [NodaTime LocalDateTime](https://nodatime.org/3.0.x/userguide/localdatetime-patterns) | `"2020-12-25T13:46:78"` |
+| LocalTime | A [NodaTime LocalTime](https://nodatime.org/3.0.x/userguide/localtime-patterns) | `"12:42:13.03101"` |
+| OffsetDateTime | A [NodaTime OffsetDateTime](https://nodatime.org/3.0.x/userguide/offsetdatetime-patterns) | `"2020-12-25T13:46:78+02:35"` |
+| OffsetDate | A [NodaTime OffsetDate](https://nodatime.org/3.0.x/userguide/offsetdate-patterns) | `"2020-12-25+02:35"` |
+| OffsetTime | A [NodaTime OffsetTime](https://nodatime.org/3.0.x/userguide/offsettime-patterns) | `"13:46:78+02:35"` |
+| Offset | A [NodeTime Offset](https://nodatime.org/3.0.x/userguide/offset-patterns) | `"+02:35"` |
+| Period | A [NodeTime Period](https://nodatime.org/3.0.x/userguide/period-patterns) | `"P-3W3DT139t"` |
+| ZonedDateTime | A [NodaTime ZonedDateTime](https://nodatime.org/3.0.x/userguide/zoneddatetime-patterns) | `"2020-12-31T19:40:13 Asia/Kathmandu +05:45"` |
+
+When returning a NodaTime type from one of our resolvers, for example a `NodaTime.Duration`, we also need to explicitly register the corresponding scalar type. In the case of a `NodaTime.Duration` this would be the `DurationType` scalar.
+
+```csharp
+public class Query
+{
+ public Duration GetDuration() => Duration.FromMinutes(3);
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType();
+```
+
+This package was originally developed by [@shoooe](https://github.com/shoooe).
+
+# Binding behavior
+
+Hot Chocolate binds most of the native .NET types automatically.
+A `System.String` is for example automatically mapped to a `StringType` in the schema.
+
+We can override these mappings by explicitly specifying type bindings.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .BindRuntimeType();
+```
+
+Furthermore, we can also bind scalars to arrays or type structures:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .BindRuntimeType();
+```
+
+Hot Chocolate only exposes the used scalars in the generated schema, keeping it simple and clean.
+
+# Custom Converters
+
+We can reuse existing scalar types and bind them to different runtime types by specifying converters.
+
+We could for example register converters between [NodaTime](https://nodatime.org/)'s `OffsetDateTime` and .NET's `DateTimeOffset` to reuse the existing `DateTimeType`.
+
+```csharp
+public class Query
+{
+ public OffsetDateTime GetDateTime(OffsetDateTime offsetDateTime)
+ {
+ return offsetDateTime;
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .BindRuntimeType()
+ .AddTypeConverter(
+ x => x.ToDateTimeOffset())
+ .AddTypeConverter(
+ x => OffsetDateTime.FromDateTimeOffset(x));
+```
+
+# Scalar Options
+
+Some scalars like `TimeSpan` or `Uuid` have options like their serialization format.
+
+We can specify these options by registering the scalar explicitly.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddType(new UuidType('D'));
+```
+
+# Custom Scalars
+
+All scalars in Hot Chocolate are defined through a `ScalarType`.
+The easiest way to create a custom scalar is to extend `ScalarType`.
+This base class already includes basic serialization and parsing logic.
+
+```csharp
+public sealed class CreditCardNumberType : ScalarType
+{
+ private readonly ICreditCardValidator _validator;
+
+ // we can inject services that have been registered
+ // with the DI container
+ public CreditCardNumberType(ICreditCardValidator validator)
+ : base("CreditCardNumber")
+ {
+ _validator = validator;
+
+ Description = "Represents a credit card number";
+ }
+
+ // is another StringValueNode an instance of this scalar
+ protected override bool IsInstanceOfType(StringValueNode valueSyntax)
+ => IsInstanceOfType(valueSyntax.Value);
+
+ // is another string .NET type an instance of this scalar
+ protected override bool IsInstanceOfType(string runtimeValue)
+ => _validator.ValidateCreditCard(runtimeValue);
+
+ public override IValueNode ParseResult(object? resultValue)
+ => ParseValue(resultValue);
+
+ // define how a value node is parsed to the string .NET type
+ protected override string ParseLiteral(StringValueNode valueSyntax)
+ => valueSyntax.Value;
+
+ // define how the string .NET type is parsed to a value node
+ protected override StringValueNode ParseValue(string runtimeValue)
+ => new StringValueNode(runtimeValue);
+
+ public override bool TryDeserialize(object? resultValue,
+ out object? runtimeValue)
+ {
+ runtimeValue = null;
+
+ if (resultValue is string s && _validator.ValidateCreditCard(s))
+ {
+ runtimeValue = s;
+ return true;
+ }
+
+ return false;
+ }
+
+ public override bool TrySerialize(object? runtimeValue,
+ out object? resultValue)
+ {
+ resultValue = null;
+
+ if (runtimeValue is string s && _validator.ValidateCreditCard(s))
+ {
+ resultValue = s;
+ return true;
+ }
+
+ return false;
+ }
+}
+```
+
+By extending `ScalarType` we have full control over serialization and parsing.
+
+```csharp
+public class CreditCardNumberType : ScalarType
+{
+ private readonly ICreditCardValidator _validator;
+
+ public CreditCardNumberType(ICreditCardValidator validator)
+ : base("CreditCardNumber")
+ {
+ _validator = validator;
+
+ Description = "Represents a credit card number";
+ }
+
+ // define which .NET type represents your type
+ public override Type RuntimeType { get; } = typeof(string);
+
+ // define which value nodes this type can be parsed from
+ public override bool IsInstanceOfType(IValueNode valueSyntax)
+ {
+ if (valueSyntax == null)
+ {
+ throw new ArgumentNullException(nameof(valueSyntax));
+ }
+
+ return valueSyntax is StringValueNode stringValueNode &&
+ _validator.ValidateCreditCard(stringValueNode.Value);
+ }
+
+ // define how a value node is parsed to the native .NET type
+ public override object ParseLiteral(IValueNode valueSyntax,
+ bool withDefaults = true)
+ {
+ if (valueSyntax is StringValueNode stringLiteral &&
+ _validator.ValidateCreditCard(stringLiteral.Value))
+ {
+ return stringLiteral.Value;
+ }
+
+ throw new SerializationException(
+ "The specified value has to be a credit card number in the format "
+ + "XXXX XXXX XXXX XXXX",
+ this);
+ }
+
+ // define how the .NET type is parsed to a value node
+ public override IValueNode ParseValue(object? runtimeValue)
+ {
+ if (runtimeValue is string s &&
+ _validator.ValidateCreditCard(s))
+ {
+ return new StringValueNode(null, s, false);
+ }
+
+ throw new SerializationException(
+ "The specified value has to be a credit card number in the format "
+ + "XXXX XXXX XXXX XXXX",
+ this);
+ }
+
+ public override IValueNode ParseResult(object? resultValue)
+ {
+ if (resultValue is string s &&
+ _validator.ValidateCreditCard(s))
+ {
+ return new StringValueNode(null, s, false);
+ }
+
+ throw new SerializationException(
+ "The specified value has to be a credit card number in the format "
+ + "XXXX XXXX XXXX XXXX",
+ this);
+ }
+
+ public override bool TrySerialize(object? runtimeValue,
+ out object? resultValue)
+ {
+ resultValue = null;
+
+ if (runtimeValue is string s &&
+ _validator.ValidateCreditCard(s))
+ {
+ resultValue = s;
+ return true;
+ }
+
+ return false;
+ }
+
+ public override bool TryDeserialize(object? resultValue,
+ out object? runtimeValue)
+ {
+ runtimeValue = null;
+
+ if (resultValue is string s &&
+ _validator.ValidateCreditCard(s))
+ {
+ runtimeValue = s;
+ return true;
+ }
+
+ return false;
+ }
+}
+```
+
+The implementation of [Hot Chocolate's own scalars](https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/Core/src/Types.Scalars) can be used as a reference for writing custom scalars.
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/subscriptions.md b/website/src/docs/hotchocolate/v15/defining-a-schema/subscriptions.md
new file mode 100644
index 00000000000..47f2f259272
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/subscriptions.md
@@ -0,0 +1,333 @@
+---
+title: "Subscriptions"
+---
+
+
+
+GraphQL subscriptions provide real-time functionality to applications by allowing clients to subscribe to specific events. When these events trigger, the server immediately sends updates to the subscribed clients.
+
+# Transport Mechanisms for GraphQL Subscriptions
+
+The method of how these updates are delivered is determined by the transport mechanism. In this section, we will discuss two popular transport mechanisms: GraphQL over WebSockets and GraphQL over Server-Sent Events (SSE).
+
+## GraphQL over WebSockets
+
+WebSockets provide a full-duplex communication channel over a single TCP connection. This means data can be sent and received simultaneously. With GraphQL, this means both queries/mutations and subscription operations can be sent over the same connection.
+
+WebSockets are widely supported in browsers and have been the de facto standard for real-time data transport in GraphQL. There are two popular protocols for GraphQL over WebSockets: [graphql-ws](https://github.com/enisdenjo/graphql-ws) and [subscription-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
+Hot Chocolate, supports both protocols.
+
+In terms of specific protocols, the recommendation is to use graphql-ws or graphql-sse over the legacy subscription-transport-ws.
+
+**Key Features:**
+
+- Full-duplex: Both the client and server can initiate communication, allowing real-time bidirectional communication.
+- Persistent connection: The connection between client and server remains open, allowing for real-time data transfer.
+- Well-supported: There are several libraries available for managing WebSocket connections and GraphQL subscriptions.
+
+## GraphQL over Server-Sent Events (SSE)
+
+Server-Sent Events (SSE) is a standard that allows a server to push real-time updates to clients over HTTP. Unlike WebSockets, SSE is a half-duplex communication channel, which means the server can send messages to the client, but not the other way around. This makes it a good fit for one-way real-time data like updates or notifications.
+
+With GraphQL, you can send regular queries and mutations over HTTP/2 and subscription updates over SSE. This combination leverages the strengths of both HTTP/2 (efficient for request-response communication) and SSE (efficient for server-to-client streaming).
+
+Another advantage of SSE is its better compatibility with firewalls compared to WebSockets. However, if you're using HTTP/1, keep in mind that SSE inherits its limitations, such as supporting no more than 7 parallel requests in the browser.
+
+[graphql-sse](https://github.com/enisdenjo/graphql-sse) is a popular library for GraphQL over SSE.
+
+**Key Features:**
+
+- Efficient for one-way real-time data: The server can push updates to the client as soon as they occur.
+- Built on HTTP: SSE is built on HTTP, simplifying handling and compatibility. It benefits from HTTP features such as automatic reconnection, HTTP/2 multiplexing, and headers/cookies support.
+- Less Complex: SSE is less complex than WebSockets as it only allows for one-way communication.
+- Better Firewall Compatibility: SSE generally encounters fewer issues with firewalls.
+
+Choosing between GraphQL over WebSockets and GraphQL over SSE depends on the specific needs of your application. If you need full-duplex, real-time communication, WebSockets may be the best choice. If you only need server-to-client real-time communication and want to take advantage of existing HTTP infrastructure, SSE could be a better option.
+
+Special thanks to Denis Badurina, @enisdenjo on [Twitter](https://twitter.com/enisdenjo) and [GitHub](https://github.com/enisdenjo). He is the creator of [graphql-http](https://github.com/enisdenjo/graphql-http), [graphql-ws](https://github.com/enisdenjo/graphql-ws) and [graphql-sse](https://github.com/enisdenjo/graphql-sse).
+
+# Usage
+
+Subscribing to an event is like writing a standard query. The only difference is the operation keyword and that we are only allowed to have one root field.
+
+```sdl
+type Subscription {
+ bookAdded: Book!
+ bookPublished(author: String!): Book!
+}
+```
+
+```graphql
+subscription {
+ bookAdded {
+ title
+ }
+}
+```
+
+A subscription type can be defined like the following.
+
+
+
+
+```csharp
+public class Subscription
+{
+ [Subscribe]
+ public Book BookAdded([EventMessage] Book book) => book;
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddSubscriptionType();
+```
+
+
+
+
+```csharp
+public class SubscriptionType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("bookAdded")
+ .Type()
+ .Resolve(context => context.GetEventMessage())
+ .Subscribe(async context =>
+ {
+ var receiver = context.Service();
+
+ ISourceStream stream =
+ await receiver.SubscribeAsync("bookAdded");
+
+ return stream;
+ });
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddSubscriptionType();
+```
+
+
+
+
+```csharp
+public class Subscription
+{
+ [Subscribe]
+ public Book BookAdded([EventMessage] Book book) => book;
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Subscription {
+ bookAdded: Book!
+ }
+
+ type Book {
+ title: String
+ author: String
+ }
+ ")
+ .BindRuntimeType();
+```
+
+
+
+
+> Warning: Only **one** subscription type can be registered using `AddSubscriptionType()`. If we want to split up our subscription type into multiple classes, we can do so using type extensions.
+>
+> [Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+A subscription type is just a regular object type, so everything that applies to an object type also applies to the subscription type (this is true for all all root types).
+
+[Learn more about object types](/docs/hotchocolate/v15/defining-a-schema/object-types)
+
+# Transport
+
+After defining the subscription type, we need to add the WebSockets middleware to our request pipeline.
+
+```csharp
+app.UseRouting();
+
+app.UseWebSockets();
+
+app.UseEndpoints(endpoints =>
+{
+ endpoints.MapGraphQL();
+});
+```
+
+To make pub/sub work, we also have to register a subscription provider. A subscription provider represents a pub/sub implementation used to handle events. Out of the box we support two subscription providers.
+
+## In-Memory Provider
+
+The In-Memory subscription provider does not need any configuration and is easily setup.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddInMemorySubscriptions();
+```
+
+## Redis Provider
+
+The Redis subscription provider enables us to run multiple instances of our Hot Chocolate GraphQL server and handle subscription events reliably.
+
+In order to use the Redis provider we have to add the `HotChocolate.Subscriptions.Redis` package.
+
+
+
+After we have added the package we can setup the Redis subscription provider.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddRedisSubscriptions((sp) => ConnectionMultiplexer.Connect("host:port"));
+```
+
+Our Redis subscription provider uses the [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis) Redis client underneath.
+
+## Postgres Provider
+
+The PostgreSQL Subscription Provider enables your GraphQL server to provide real-time updates to your clients using PostgreSQL's native `LISTEN/NOTIFY` mechanism. This provider is ideal for applications that already use PostgreSQL and want to avoid the overhead of running a separate pub/sub service.
+
+In order to use the PostgreSQL provider we have to add the `HotChocolate.Subscriptions.Postgres` package.
+
+```bash
+dotnet add package HotChocolate.Subscriptions.Postgres
+```
+
+To enable Postgres subscriptions with your HotChocolate server, add `AddPostgresSubscriptions` to your GraphQL server configuration:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType() // every GraphQL server needs a query
+ .AddSubscriptionType()
+ .AddPostgresSubscriptions((sp, options) => options.ConnectionFactory = ct => /*create you connection*/);
+```
+
+### Options
+
+`PostgresSubscriptionOptions` encapsulates options for configuring the Postgres subscription provider. The properties included in this class are:
+
+1. `ConnectionFactory`: A function used to create a new, long-lived connection. The connection should have the following configuration to work optimally:
+
+ - `KeepAlive=30`: Sets a keep alive interval to keep the connection alive
+ - `Pooling=false`: Disables pooling as it is not needed
+ - `Enlist=false`: Ensures subscriptions run in the background and are not enlisted into any transaction
+
+2. `ChannelName`: Specifies the name of the Postgres channel used to send/receive messages. The default value is "hotchocolate_subscriptions".
+3. `MaxSendBatchSize`: Sets the maximum number of messages sent in one batch. The default value is 256.
+4. `MaxSendQueueSize`: Determines the maximum number of messages that can be queued for sending. If the queue is full, the subscription will wait until there is available space. The default value is 2048.
+5. `SubscriptionOptions`: Options used to configure the subscriptions.
+
+Here's an example of creating a connection factory suitable for long-lived connections:
+
+```csharp
+var builder = new NpgsqlDataSourceBuilder(connectionString);
+
+// we do not need pooling for long running connections
+builder.ConnectionStringBuilder.Pooling = false;
+// we set the keep alive to 30 seconds
+builder.ConnectionStringBuilder.KeepAlive = 30;
+// as these tasks often run in the background we do not want to enlist them so they do not
+// interfere with the main transaction
+builder.ConnectionStringBuilder.Enlist = false;
+
+var dataSource = builder.Build();
+```
+
+# Publishing Events
+
+To publish events and trigger subscriptions, we can use the `ITopicEventSender`. The `ITopicEventSender` is an abstraction for the registered event publishing provider. Using this abstraction allows us to seamlessly switch between subscription providers, when necessary.
+
+Most of the time we will be publishing events for successful mutations. Therefore we can simply inject the `ITopicEventSender` into our mutations like we would with every other `Service`. Of course we can not only publish events from mutations, but everywhere we have access to the `ITopicEventSender` through the DI Container.
+
+```csharp
+public class Mutation
+{
+ public async Book AddBook(Book book, ITopicEventSender sender)
+ {
+ await sender.SendAsync("BookAdded", book);
+
+ // Omitted code for brevity
+ }
+}
+```
+
+In the example the `"BookAdded"` is the topic we want to publish to, and `book` is our payload. Even though we have used a string as the topic, we do not have to. Any other type works just fine.
+
+But where is the connection between `"BookAdded"` as a topic and the subscription type? By default, Hot Chocolate will try to map the topic to a field of the subscription type. If we want to make this binding less error-prone, we could do the following.
+
+```csharp
+await sender.SendAsync(nameof(Subscription.BookAdded), book);
+```
+
+If we do not want to use the method name, we could use the `Topic` attribute.
+
+```csharp
+public class Subscription
+{
+ [Subscribe]
+ [Topic("ExampleTopic")]
+ public Book BookAdded([EventMessage] Book book) => book;
+}
+
+public async Book AddBook(Book book, ITopicEventSender sender)
+{
+ await sender.SendAsync("ExampleTopic", book);
+
+ // Omitted code for brevity
+}
+```
+
+## Dynamic Topics
+
+We can even use the `Topic` attribute on dynamic arguments of the subscription field.
+
+```csharp
+public class Subscription
+{
+ [Subscribe]
+ // The topic argument must be in the format "{argument}"
+ // Using string interpolation and nameof is a good way to reference the argument name properly
+ [Topic($"{{{nameof(author)}}}")]
+ public Book BookPublished(string author, [EventMessage] Book book)
+ => book;
+}
+
+public async Book PublishBook(Book book, ITopicEventSender sender)
+{
+ await sender.SendAsync(book.Author, book);
+
+ // Omitted code for brevity
+}
+```
+
+## ITopicEventReceiver
+
+If more complex topics are required, we can use the `ITopicEventReceiver`.
+
+```csharp
+public class Subscription
+{
+ public ValueTask> SubscribeToBooks(ITopicEventReceiver receiver)
+ => receiver.SubscribeAsync("ExampleTopic");
+
+ [Subscribe(With = nameof(SubscribeToBooks))]
+ public Book BookAdded([EventMessage] Book book)
+ => book;
+}
+```
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/unions.md b/website/src/docs/hotchocolate/v15/defining-a-schema/unions.md
new file mode 100644
index 00000000000..30b679eae0f
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/unions.md
@@ -0,0 +1,185 @@
+---
+title: "Unions"
+---
+
+A union type represents a set of object types. It is very similar to an [interface](/docs/hotchocolate/v15/defining-a-schema/interfaces), except that there is no requirement for common fields between the specified types.
+
+```sdl
+type TextContent {
+ text: String!
+}
+
+type ImageContent {
+ imageUrl: String!
+ height: Int!
+}
+
+union PostContent = TextContent | ImageContent
+```
+
+Clients can query fields returning a union like the following.
+
+```graphql
+{
+ content {
+ ... on TextContent {
+ text
+ }
+ ... on ImageContent {
+ imageUrl
+ }
+ }
+}
+```
+
+Learn more about unions [here](https://graphql.org/learn/schema/#union-types).
+
+# Usage
+
+Unions can be defined like the following.
+
+
+
+
+We can use a marker interface (or an abstract class) to define object types as part of a union.
+
+```csharp
+[UnionType("PostContent")]
+public interface IPostContent
+{
+}
+
+public class TextContent : IPostContent
+{
+ public string Text { get; set; }
+}
+
+public class ImageContent : IPostContent
+{
+ public string ImageUrl { get; set; }
+
+ public int Height { get; set; }
+}
+
+public class Query
+{
+ public IPostContent GetContent()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType()
+ .AddType()
+ .AddType();
+```
+
+
+
+
+```csharp
+public class TextContent
+{
+ public string Text { get; set; }
+}
+
+public class TextContentType : ObjectType
+{
+}
+
+public class ImageContent
+{
+ public string ImageUrl { get; set; }
+
+ public int Height { get; set; }
+}
+
+public class ImageContentType : ObjectType
+{
+}
+
+public class PostContentType : UnionType
+{
+ protected override void Configure(IUnionTypeDescriptor descriptor)
+ {
+ descriptor.Name("PostContent");
+
+ // The object types that belong to this union
+ descriptor.Type();
+ descriptor.Type();
+ }
+}
+
+public class Query
+{
+ public object GetContent()
+ {
+ // Omitted code for brevity
+ }
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetContent(default))
+ .Type();
+ }
+}
+```
+
+Since the types are already registered within the union, we do not have to register them again in our `Program` class.
+
+We can use a marker interface, as in the implementation-first approach, to type our union definition: `UnionType`
+
+
+
+
+```csharp
+public class TextContent
+{
+ public string Text { get; set; }
+}
+
+public class ImageContent
+{
+ public string ImageUrl { get; set; }
+
+ public int Height { get; set; }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ content: PostContent
+ }
+
+ type TextContent {
+ text: String!
+ }
+
+ type ImageContent {
+ imageUrl: String!
+ height: Int!
+ }
+
+ union PostContent = TextContent | ImageContent
+ ")
+ .BindRuntimeType()
+ .BindRuntimeType()
+ .AddResolver("Query", "content", (context) =>
+ {
+ // Omitted code for brevity
+ });
+```
+
+
+
diff --git a/website/src/docs/hotchocolate/v15/defining-a-schema/versioning.md b/website/src/docs/hotchocolate/v15/defining-a-schema/versioning.md
new file mode 100644
index 00000000000..bbe39def2ff
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/defining-a-schema/versioning.md
@@ -0,0 +1,75 @@
+---
+title: "Versioning"
+---
+
+Whilst we could version our GraphQL API similar to REST, i.e. `/graphql/v1`, it is not a best practice and often unnecessary.
+
+Many changes to a GraphQL schema are non-breaking. We can freely add new types and extend existing types with new fields. This does not break existing queries.
+However removing a field or changing its nullability does.
+
+Instead of removing a field immediately and possibly breaking existing consumers of our API, fields can be marked as deprecated in our schema. This signals to consumers that the field will be removed in the future and they need to adapt before then.
+
+```sdl
+type Query {
+ users: [User] @deprecated("Use the `authors` field instead")
+ authors: [User]
+}
+
+```
+
+# Deprecation
+
+You can deprecate output fields, input fields, arguments and enum values.
+
+
+
+
+```csharp
+public class Query
+{
+ [GraphQLDeprecated("Use the `authors` field instead")]
+ public User[] GetUsers()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+> Note: .NET's `[Obsolete("reason")]` attribute is handled in the same way as `[GraphQLDeprecated("reason")]`.
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .Deprecated("Use the `authors` field instead")
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ users: [User] @deprecated(""Use the `authors` field instead"")
+ }
+ ");
+```
+
+
+
+
+> Warning: You can not deprecate non-null arguments or input fields without a default value.
diff --git a/website/src/docs/hotchocolate/v15/execution-engine/field-middleware.md b/website/src/docs/hotchocolate/v15/execution-engine/field-middleware.md
new file mode 100644
index 00000000000..16fb25e83b9
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/execution-engine/field-middleware.md
@@ -0,0 +1,299 @@
+---
+title: Field middleware
+---
+
+The field middleware is one of the fundamental components in Hot Chocolate. It allows you to create reuseable logic that can be run before or after a field resolver. Field middleware is composable, so you can specify multiple middleware and they will be executed in order. The field resolver is always the last element in this middleware chain.
+
+Each field middleware only knows about the next element in the chain and can choose to
+
+- execute logic before it
+- execute logic after all later components (including the field resolver) have been run
+- not execute the next component
+
+Each field middleware also has access to an `IMiddlewareContext`. It implements the `IResolverContext` interface so you can use all of the `IResolverContext` APIs in your middleware, similarly to how you would use them in your resolver. There are also some special properties like the `Result`, which holds the resolver or middleware computed result.
+
+# Middleware order
+
+If you have used Hot Chocolate's data middleware before you might have encountered warnings about the order of middleware. The order is important, since it determines in which order the middleware are executed, e.g. in which order the resolver result is being processed.
+
+Take the `UsePagination` and `UseFiltering` middleware for example: Does it make sense to first paginate and then filter? No. It should first be filtered and then paginated. That's why the correct order is `UsePagination` > `UseFiltering`.
+
+```csharp
+descriptor
+ .UsePagination()
+ .UseFiltering()
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+```
+
+But hold up, isn't this the opposite order of what we've just described?
+
+Lets visualize the middleware chain to understand why it is indeed the correct order.
+
+```mermaid
+sequenceDiagram
+ UsePagination->>UseFiltering: next(context)
+ UseFiltering->>Resolver: next(context)
+ Resolver->>UseFiltering: Result of the Resolver
+ UseFiltering->>UsePagination: Result of UseFiltering
+```
+
+As you can see the result of the resolver flows backwards through the middleware. So the middleware is first invoked in the order they were defined, but the result produced by the last middleware, the field resolver, is sent back to first middleware in reverse order.
+
+# Definition
+
+Field middleware can be defined either as a delegate or as a separate type. In both cases we gain access to a `FieldDelegate`, which allows us to invoke the next middleware, and the `IMiddlewareContext`.
+
+By awaiting the `FieldDelegate` we are waiting for the completion of all of the middleware that might come after the current middleware, including the actual field resolver.
+
+## Field middleware delegate
+
+A field middleware delegate can be defined using code-first APIs.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("example")
+ .Use(next => async context =>
+ {
+ // Code up here is executed before the following middleware
+ // and the actual field resolver
+
+ // This invokes the next middleware
+ // or if we are at the last middleware the field resolver
+ await next(context);
+
+ // Code down here is executed after all later middleware
+ // and the actual field resolver has finished executing
+ })
+ .Resolve(context =>
+ {
+ // Omitted for brevity
+ });
+ }
+}
+```
+
+### Reusing the middleware delegate
+
+As it's shown above the middleware is only applied to the `example` field on the `Query` type, but what if you want to use this middleware in multiple places?
+
+You can simply create an extension method for the `IObjectFieldDescriptor`.
+
+```csharp
+public static class MyMiddlewareObjectFieldDescriptorExtension
+{
+ public static IObjectFieldDescriptor UseMyMiddleware(
+ this IObjectFieldDescriptor descriptor)
+ {
+ descriptor
+ .Use(next => async context =>
+ {
+ // Omitted code for brevity
+
+ await next(context);
+
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+> Note: We recommend sticking to the convention of prepending `Use` to your extension method to indicate that it is applying a middleware.
+
+You can now use this middleware in different places throughout your schema definition.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("example")
+ .UseMyMiddleware()
+ .Resolve(context =>
+ {
+ // Omitted for brevity
+ });
+ }
+}
+```
+
+## Field middleware as a class
+
+If you do not like using a delegate, you can also create a dedicated class for your middleware.
+
+```csharp
+public class MyMiddleware
+{
+ private readonly FieldDelegate _next;
+
+ public MyMiddleware(FieldDelegate next)
+ {
+ _next = next;
+ }
+
+ // this method must be called InvokeAsync or Invoke
+ public async Task InvokeAsync(IMiddlewareContext context)
+ {
+ // Code up here is executed before the following middleware
+ // and the actual field resolver
+
+ // This invokes the next middleware
+ // or if we are at the last middleware the field resolver
+ await _next(context);
+
+ // Code down here is executed after all later middleware
+ // and the actual field resolver has finished executing
+ }
+}
+```
+
+If you need to access services you can either inject them via the constructor, if they are singleton, or as an argument of the `InvokeAsync` method, if they have a scoped or transient lifetime.
+
+```csharp
+public class MyMiddleware
+{
+ private readonly FieldDelegate _next;
+ private readonly IMySingletonService _singletonService;
+
+ public MyMiddleware(FieldDelegate next, IMySingletonService singletonService)
+ {
+ _next = next;
+ _singletonService = singletonService;
+ }
+
+ public async Task InvokeAsync(IMiddlewareContext context,
+ IMyScopedService scopedService)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+The ability to add additional arguments to the `InvokeAsync` method is the reason why there isn't a contract like an interface or a base class for field middleware.
+
+### Usage
+
+Now that you've defined the middleware as a class we need to still apply it to a field.
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("example")
+ .Use()
+ .Resolve(context =>
+ {
+ // Omitted for brevity
+ });
+ }
+}
+```
+
+While an extension method like `UseMyMiddleware` on the `IObjectFieldDescriptor` doesn't make as much sense for `Use` in contrast to the middleware delegate, we still recommend creating one as shown [here](#reusing-the-middleware-delegate). The reason being that you can make changes to this middleware more easily in the future without potentially having to change all places this middleware is being used in.
+
+If you need to pass an additional custom argument to the middleware you can do so using the factory overload of the `Use` method.
+
+```csharp
+descriptor
+ .Field("example")
+ .Use((provider, next) => new MyMiddleware(next, "custom",
+ provider.GetRequiredService()));
+```
+
+# Usage as an attribute
+
+Up until now we have only worked with code-first APIs to create the field middleware. What if you want to apply your middleware to a field resolver defined using the implementation-first approach?
+
+You can create a new attribute inheriting from `ObjectFieldDescriptorAttribute` and call or create your middleware inside of the `OnConfigure` method.
+
+> Note: Attribute order is not guaranteed in C#, so we, in the case of middleware attributes, use the `CallerLineNumberAttribute` to inject the C# line number at compile time. The line number is used as an order. We do not recommend inheriting middleware attributes from a base method or property since this can lead to confusion about ordering. Look at the example below to see how we infer the order. When inheriting from middleware, attributes always pass through the order argument. Further, indicate with the `Use` verb that your attribute is a middleware attribute.
+
+```csharp
+public class UseMyMiddlewareAttribute : ObjectFieldDescriptorAttribute
+{
+ public UseMyMiddlewareAttribute([CallerLineNumber] int order = 0)
+ {
+ Order = order;
+ }
+
+ protected override void OnConfigure(IDescriptorContext context,
+ IObjectFieldDescriptor descriptor, MemberInfo member)
+ {
+ descriptor.UseMyMiddleware();
+ }
+}
+
+```
+
+The attribute can then be used like the following.
+
+```csharp
+public class Query
+{
+ [UseMyMiddleware]
+ public string MyResolver()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+# Accessing the resolver result
+
+The `IMiddlewareContext` conveniently contains a `Result` property that can be used to access the field resolver result.
+
+```csharp
+descriptor
+ .Use(next => async context =>
+ {
+ await next(context);
+
+ // It only makes sense to access the result after calling
+ // next(context), i.e. after the field resolver and any later
+ // middleware has finished executing.
+ object? result = context.Result;
+
+ // If needed you can now narrow down the type of the result
+ // using pattern matching and continue with the typed result
+ if (result is string stringResult)
+ {
+ // Work with the stringResult
+ }
+ });
+```
+
+A middleware can also set or override the result by assigning the `context.Result` property.
+
+> Note: The field resolver will only execute if no result has been produced by one of the preceding field middleware. If any middleware has set the `Result` property on the `IMiddlewareContext`, the field resolver will be skipped.
+
+# Short-circuiting
+
+In some cases we might want to short-circuit the execution of field middleware / the field resolver. For this we can simply not call the `FieldDelegate` (`next`).
+
+```csharp
+descriptor
+ .Use(next => context =>
+ {
+ if(context.Parent() is IDictionary dict)
+ {
+ context.Result = dict[context.Field.Name];
+
+ // We are not executing any of the later middleware
+ // or the field resolver
+ return Task.CompletedTask;
+ }
+ else
+ {
+ return next(context);
+ }
+ })
+```
diff --git a/website/src/docs/hotchocolate/v15/execution-engine/index.md b/website/src/docs/hotchocolate/v15/execution-engine/index.md
new file mode 100644
index 00000000000..ed7a41982cc
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/execution-engine/index.md
@@ -0,0 +1,22 @@
+---
+title: Overview
+description: In this section, we will learn about the Hot Chocolate GraphQL server execution engine.
+---
+
+# Request Middleware
+
+The GraphQL execution is abstracted into a request pipeline composed of many request middleware. Each request middleware represents one part of executing a GraphQL request, like the parsing of the GraphQL request document or the semantical validation of the GraphQL request document.
+
+
+
+# Field middleware
+
+Field middleware allows us to create reusable logic that is run before or after a resolver. It also allows us to access or even modify the result produced by a resolver.
+
+[Learn more about field middleware](/docs/hotchocolate/v15/execution-engine/field-middleware)
+
+# Resolver Compiler
+
+The resolver compiler will compile for each resolver an optimized resolver pipeline. The resolver compiler can be customized by providing parameter expression builder.
+
+
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/dataloader.md b/website/src/docs/hotchocolate/v15/fetching-data/dataloader.md
new file mode 100644
index 00000000000..ebe348005ab
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/dataloader.md
@@ -0,0 +1,278 @@
+---
+title: "DataLoader"
+---
+
+> If you want to read more about data loaders in general, you can head over to Facebook's [GitHub repository](https://github.com/facebook/dataloader).
+
+Every data fetching technology suffers the _n+1_ problem.
+The difference between GraphQL and e.g. REST is, that the _n+1_ problem occurs on the server, rather than on the client.
+The clear benefit is, that we only have to deal with this problem once on the server, rather than on every client.
+
+To depict the issue that data loaders solve in this context, let assume we have this schema:
+
+```sdl
+type Query {
+ person(id: ID): Person
+}
+
+type Person {
+ id: ID
+ name: String
+ friends: [Person]
+}
+```
+
+The above schema allows to fetch a person by its internal identifier and each person has a list of friends that is represented by a list of persons.
+
+A query against the above schema could look like the following:
+
+```graphql
+{
+ a: person(id: "a") {
+ name
+ }
+
+ b: person(id: "b") {
+ name
+ }
+}
+```
+
+The above request fetches two persons in one go without the need to call the backend twice. The problem with the GraphQL backend is that field resolvers are atomic and do not have any knowledge about the query as a whole. So, a field resolver does not know that it will be called multiple times in parallel to fetch similar or equal data from the same data source.
+
+The idea of a DataLoader is to batch these two requests into one call to the database.
+
+Let's look at some code to understand what data loaders are doing. First, let's have a look at how we would write our field resolver without data loaders:
+
+```csharp
+public async Task GetPerson(string id, IPersonRepository repository)
+{
+ return await repository.GetPersonById(id);
+}
+```
+
+The above example would result in two calls to the person repository that would then fetch the persons one by one from our data source.
+
+If you think that through you see that each GraphQL request would cause multiple requests to our data source resulting in sluggish performance and unnecessary round-trips to our data source.
+
+This means that we reduced the round-trips from our client to our server with GraphQL but still have the round-trips between the data sources and the service layer.
+
+With data loaders we can now centralize the data fetching and reduce the number of round trips to our data source.
+
+Instead of fetching the data from the repository directly, we fetch the data from the data loader.
+The data loader batches all the requests together into one request to the database.
+
+```csharp
+// This is one way of implementing a data loader. You will find the different ways of declaring
+// data loaders further down the page.
+public class PersonBatchDataLoader : BatchDataLoader
+{
+ private readonly IPersonRepository _repository;
+
+ public PersonBatchDataLoader(
+ IPersonRepository repository,
+ IBatchScheduler batchScheduler,
+ DataLoaderOptions? options = null)
+ : base(batchScheduler, options)
+ {
+ _repository = repository;
+ }
+
+ protected override async Task> LoadBatchAsync(
+ IReadOnlyList keys,
+ CancellationToken cancellationToken)
+ {
+ // instead of fetching one person, we fetch multiple persons
+ var persons = await _repository.GetPersonByIds(keys);
+ return persons.ToDictionary(x => x.Id);
+ }
+}
+
+public class Query
+{
+ public async Task GetPerson(
+ string id,
+ PersonBatchDataLoader dataLoader)
+ => await dataLoader.LoadAsync(id);
+}
+```
+
+# Execution
+
+With a data loader, you can fetch entities with a key.
+These are the two generics you have in the class data loaders:
+
+```csharp
+public class BatchDataLoader
+```
+
+`TId` is used as an identifier of `TEntity`. `TId` is the type of the values you put into `LoadAsync`.
+
+The execution engine of Hot Chocolate tries to batch as much as possible.
+It executes resolvers until the queue is empty and then triggers the data loader to resolve the data for the waiting resolvers.
+
+# Data Consistency
+
+DataLoader do not only batch calls to the database, they also cache the database response.
+A data loader guarantees data consistency in a single request.
+If you load an entity with a data loader in your request more than once, it is given that these two entities are equivalent.
+
+Data loaders do not fetch an entity if there is already an entity with the requested key in the cache.
+
+# Types of DataLoader
+
+In Hot Chocolate you can declare data loaders in two different ways.
+You can separate the data loading concern into separate classes or you can use a delegate in the resolver to define data loaders on the fly.
+Below you will find the different types of data loaders with examples for class and delegate definition.
+
+## Batch DataLoader
+
+> One - To - One, usually used for fields like `personById` or one to one relations
+
+The batch data loader collects requests for entities and sends them as a batch request to the data source. Moreover, the data loader caches the retrieved entries within a request.
+
+The batch data loader gets the keys as `IReadOnlyList` and returns an `IReadOnlyDictionary`.
+
+### Class
+
+```csharp
+public class PersonBatchDataLoader : BatchDataLoader
+{
+ private readonly IPersonRepository _repository;
+
+ public PersonBatchDataLoader(
+ IPersonRepository repository,
+ IBatchScheduler batchScheduler,
+ DataLoaderOptions? options = null)
+ : base(batchScheduler, options)
+ {
+ _repository = repository;
+ }
+
+ protected override async Task> LoadBatchAsync(
+ IReadOnlyList keys,
+ CancellationToken cancellationToken)
+ {
+ // instead of fetching one person, we fetch multiple persons
+ var persons = await _repository.GetPersonByIds(keys);
+ return persons.ToDictionary(x => x.Id);
+ }
+}
+
+public class Query
+{
+ public async Task GetPerson(
+ string id,
+ PersonBatchDataLoader dataLoader)
+ => await dataLoader.LoadAsync(id);
+}
+```
+
+### Delegate
+
+```csharp
+public Task GetPerson(
+ string id,
+ IResolverContext context,
+ IPersonRepository repository)
+{
+ return context.BatchDataLoader(
+ async (keys, ct) =>
+ {
+ var result = await repository.GetPersonByIds(keys);
+ return result.ToDictionary(x => x.Id);
+ })
+ .LoadAsync(id);
+}
+```
+
+_An example with the **Batch DataLoader** can be found [here](https://github.com/ChilliCream/graphql-workshop/blob/master/code/complete/GraphQL/DataLoader/TrackByIdDataLoader.cs)._
+
+## Group DataLoader
+
+> One - To - Many, usually used for fields like `personsByLastName` or one to many relations
+
+The group data loader is also a batch data loader but instead of returning one entity per key, it returns multiple entities per key. As with the batch data loader retrieved collections are cached within a request.
+
+The group data loader gets the keys as `IReadOnlyList` and returns an `ILookup`.
+
+### Class
+
+```csharp
+public class PersonsByLastNameDataloader
+ : GroupedDataLoader
+{
+ private readonly IPersonRepository _repository;
+
+ public PersonsByLastNameDataloader(
+ IPersonRepository repository,
+ IBatchScheduler batchScheduler,
+ DataLoaderOptions? options = null)
+ : base(batchScheduler, options)
+ {
+ _repository = repository;
+ }
+
+ protected override async Task> LoadGroupedBatchAsync(
+ IReadOnlyList names,
+ CancellationToken cancellationToken)
+ {
+ var persons = await _repository.GetPersonsByLastName(names);
+ return persons.ToLookup(x => x.LastName);
+ }
+}
+
+public class Query
+{
+ public async Task> GetPersonByLastName(
+ string lastName,
+ PersonsByLastNameDataloader dataLoader)
+ => await dataLoader.LoadAsync(lastName);
+}
+```
+
+### Delegate
+
+```csharp
+public Task> GetPersonByLastName(
+ string lastName,
+ IResolverContext context,
+ IPersonRepository repository)
+{
+ return context.GroupDataLoader(
+ async (keys, ct) =>
+ {
+ var result = await repository.GetPersonsByLastName(keys);
+ return result.ToLookup(t => t.LastName);
+ })
+ .LoadAsync(lastName);
+}
+```
+
+## Cache DataLoader
+
+> No batching, just caching. This data loader is used rarely. You most likely want to use the batch data loader.
+
+The cache data loader is the easiest to implement since there is no batching involved. You can just use the initial `GetPersonById` method. We do not get the benefits of batching with this one, but if in a query graph the same entity is resolved twice we will load it only once from the data source.
+
+```csharp
+public Task GetPerson(string id, IResolverContext context, IPersonRepository repository)
+{
+ return context.CacheDataLoader("personById", keys => repository.GetPersonById(keys)).LoadAsync(id);
+}
+```
+
+# Stacked DataLoader Calls
+
+This is more like an edge case that is supported than a certain type of data loader. Sometimes we have more complex resolvers that might first fetch data from one data loader and use that to fetch data from the next.
+
+```csharp
+public Task> GetCustomers(
+ string personId,
+ PersonByIdDataLoader personByIdDataLoader,
+ CustomerByIdsDataLoader customerByIdsDataloader)
+{
+ Person person = await personByIdDataLoader.LoadAsync(personId);
+ return await customerByIdsDataloader.LoadAsync(person.CustomerIds);
+}
+```
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-databases.md b/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-databases.md
new file mode 100644
index 00000000000..b6bf3601402
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-databases.md
@@ -0,0 +1,132 @@
+---
+title: "Fetching from Databases"
+---
+
+In this section, you find a simple example on how you can fetch data from a database and expose it as a GraphQL API.
+
+**Hot Chocolate is not bound to a specific database, pattern or architecture.**
+[We do have a few integrations](/docs/hotchocolate/v15/integrations), that help with a variety of databases, though these are just additions on top of HotChocolate.
+You can couple your business logic close to the GraphQL server, or cleanly decouple your domain layer from the GraphQL layer over abstractions.
+The GraphQL server only knows its schema, types and resolvers, what you do in these resolvers and what types you expose, is up to you.
+
+In this example, we will directly fetch data from MongoDB in a resolver.
+
+# Setting up the Query
+
+The query type in a GraphQL schema is the root type. Each field defined on this type is available at the root of a query.
+If a field is requested, the resolver of the field is called.
+The data of this resolver is used for further execution.
+If you return a scalar, value (e.g. `string`, `int` ...) the value is serialized and added to the response.
+If you return an object, this object is the parent of the resolver in the subtree.
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task GetBookById(IMongoCollection collection, Guid id)
+ {
+ return collection.Find(x => x.Id == id).FirstOrDefaultAsync();
+ }
+}
+
+// Book.cs
+public class Book
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+// Program.cs
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task GetBookById(IMongoCollection collection, Guid id)
+ {
+ return collection.Find(x => x.Id == id).FirstOrDefaultAsync();
+ }
+}
+
+// QueryType.cs
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetBookById(default!, default!))
+ .Type();
+ }
+}
+
+// Book.cs
+public class Book
+{
+ public string Title { get; set; }
+
+ public string Author { get; set; }
+}
+
+// BookType.cs
+public class BookType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.Title)
+ .Type();
+
+ descriptor
+ .Field(f => f.Author)
+ .Type();
+ }
+}
+
+// Program.cs
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task GetBookById(IMongoCollection collection, Guid id)
+ {
+ return collection.Find(x => x.Id == id).FirstOrDefaultAsync();
+ }
+}
+
+// Program.cs
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ bookById(id: Uuid): Book
+ }
+
+ type Book {
+ title: String
+ author: String
+ }
+ ")
+ .BindRuntimeType();
+```
+
+
+
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-rest.md b/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-rest.md
new file mode 100644
index 00000000000..cfc2ab3b305
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/fetching-from-rest.md
@@ -0,0 +1,230 @@
+---
+title: "Fetching from REST"
+description: In this section, we will cover how you can easily integrate a REST API into your GraphQL API.
+---
+
+If you want to have an outlook into the upcoming native REST integration with Hot Chocolate 13 you can head over to YouTube and have a look.
+
+
+
+GraphQL has a strongly-typed type system and therefore also has to know the dotnet runtime types of the data it returns in advance.
+
+The easiest way to integrate a REST API is, to define an OpenAPI specification for it.
+OpenAPI describes what data a REST endpoint returns.
+You can automatically generate a dotnet client for this API and integrate it into your schema.
+
+# OpenAPI in .NET
+
+If you do not have an OpenAPI specification for your REST endpoint yet, you can easily add it to your API.
+There are two major OpenAPI implementations in dotnet: [NSwag](http://nswag.org) and [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore).
+Head over to the [official ASP.NET Core](https://docs.microsoft.com/aspnet/core/tutorials/web-api-help-pages-using-swagger) documentation to see how it is done.
+
+In this example, we will use [the official example of Swashbuckle](https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/tutorials/getting-started-with-swashbuckle.md).
+When you start this project, you can navigate to the [Swagger UI](http://localhost:5000/swagger).
+
+This REST API covers a simple Todo app.
+We will expose `todos` and `todoById` in our GraphQL API.
+
+# Generating a client
+
+Every REST endpoint that supports OpenAPI, can easily be wrapped with a fully typed client.
+Again, you have several options on how you generate your client.
+You can generate your client from the OpenAPI specification of your endpoint, during build or even with external tools with GUI.
+Have a look here and see what fits your use case the best:
+
+- [NSwag Code Generation](https://docs.microsoft.com/aspnet/core/tutorials/getting-started-with-nswag?tabs=visual-studio#code-generation)
+
+In this example, we will use the NSwag dotnet tool.
+First, we need to create a tool manifest.
+Switch to your GraphQL project and execute
+
+```bash
+dotnet new tool-manifest
+```
+
+Then we install the NSwag tool
+
+```bash
+dotnet tool install NSwag.ConsoleCore --version 13.10.9
+```
+
+You then have to get the `swagger.json` from your REST endpoint
+
+```bash
+curl -o swagger.json http://localhost:5000/swagger/v1/swagger.json
+```
+
+Now you can generate the client from the `swagger.json`.
+
+```bash
+dotnet nswag swagger2csclient /input:swagger.json /classname:TodoService /namespace:TodoReader /output:TodoService.cs
+```
+
+The code generator generated a new file called `TodoService.cs`.
+In this file, you will find the client for your REST API.
+
+The generated needs `Newtonsoft.Json`.
+Make sure to also add this package by executing:
+
+
+
+# Exposing the API
+
+You will have to register the client in the dependency injection of your GraphQL service.
+To expose the API you can inject the generated client into your resolvers.
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task> GetTodosAsync(
+ TodoService service,
+ CancellationToken cancellationToken)
+ {
+ return service.GetAllAsync(cancellationToken);
+ }
+
+ public Task GetTodoByIdAsync(
+ TodoService service,
+ long id,
+ CancellationToken cancellationToken)
+ {
+ return service.GetByIdAsync(id, cancellationToken);
+ }
+}
+
+// Program.cs
+builder.Services.AddHttpClient();
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task> GetTodosAsync(
+ TodoService service,
+ CancellationToken cancellationToken)
+ {
+ return service.GetAllAsync(cancellationToken);
+ }
+
+ public Task GetTodoByIdAsync(
+ TodoService service,
+ long id,
+ CancellationToken cancellationToken)
+ {
+ return service.GetByIdAsync(id, cancellationToken);
+ }
+}
+
+// QueryType.cs
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetTodoByIdAsync(default!, default!, default!))
+ .Type();
+
+ descriptor
+ .Field(f => f.GetTodosAsync(default!, default!))
+ .Type>();
+ }
+}
+
+// TodoType.cs
+public class TodoType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.Id)
+ .Type();
+
+ descriptor
+ .Field(f => f.Name)
+ .Type();
+
+ descriptor
+ .Field(f => f.IsComplete)
+ .Type();
+ }
+}
+
+// Program.cs
+builder.Services
+ .AddGraphQLServer()
+ .AddQueryType();
+```
+
+
+
+
+```csharp
+// Query.cs
+public class Query
+{
+ public Task> GetTodosAsync(
+ TodoService service,
+ CancellationToken cancellationToken)
+ {
+ return service.GetAllAsync(cancellationToken);
+ }
+
+ public Task GetTodoByIdAsync(
+ TodoService service,
+ long id,
+ CancellationToken cancellationToken)
+ {
+ return service.GetByIdAsync(id, cancellationToken);
+ }
+}
+
+// Program.cs
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ todos: [TodoItem!]!
+ todoById(id: Uuid): TodoItem
+ }
+
+ type TodoItem {
+ id: Long
+ name: String
+ isCompleted: Boolean
+ }
+ ")
+ .BindRuntimeType();
+```
+
+
+
+
+You can now head over to Nitro on your GraphQL Server (/graphql) and query `todos`:
+
+```graphql
+{
+ todoById(id: 1) {
+ id
+ isComplete
+ name
+ }
+ todos {
+ id
+ isComplete
+ name
+ }
+}
+```
+
+
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/filtering.md b/website/src/docs/hotchocolate/v15/fetching-data/filtering.md
new file mode 100644
index 00000000000..93c906ad432
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/filtering.md
@@ -0,0 +1,790 @@
+---
+title: Filtering
+---
+
+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 that are applied to `IQueryable`.
+Hot Chocolate by default will inspect your .NET model and infer the possible filter operations from it.
+Filters use `IQueryable` (`IEnumerable`) by default, but you can also easily customize them to use other interfaces.
+
+The following type would yield the following filter operations:
+
+```csharp
+public class Foo
+{
+ public string Bar { get; set; }
+}
+```
+
+```sdl
+input FooFilterInput {
+ and: [FooFilterInput!]
+ or: [FooFilterInput!]
+ name: StringOperationFilterInput
+}
+
+input StringOperationFilterInput {
+ and: [StringOperationFilterInput!]
+ or: [StringOperationFilterInput!]
+ eq: String
+ neq: String
+ contains: String
+ ncontains: String
+ in: [String]
+ nin: [String]
+ startsWith: String
+ nstartsWith: String
+ endsWith: String
+ nendsWith: String
+}
+```
+
+# Getting started
+
+Filtering is part of the `HotChocolate.Data` package.
+
+
+
+To use filtering you need to register it on the schema:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ // Your schema configuration
+ .AddFiltering();
+```
+
+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.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+```
+
+
+
+
+```csharp
+public class Query
+{
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field(f => f.GetUsers(default))
+ .Type>>()
+ .UseFiltering();
+ }
+}
+```
+
+
+
+
+⚠️ Schema-first does currently not support filtering!
+
+
+
+
+> ⚠️ **Note:** If you use more than one middleware, keep in mind that **ORDER MATTERS**. The correct order is UsePaging > UseProjections > UseFiltering > UseSorting
+
+# Customization
+
+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 input types. Just extend the base class `FilterInputType` and override the descriptor method.
+
+`IFilterInputTypeDescriptor` supports most of the methods of `IInputTypeDescriptor`. By default filters for all fields of the type are generated.
+If you do want to specify the filters by yourself you can change this behavior with `BindFields`, `BindFieldsExplicitly` or `BindFieldsImplicitly`.
+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)`.
+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.BindFieldsExplicitly();
+ descriptor.Field(f => f.Name).Name("custom_name");
+ }
+}
+```
+
+If you want to limit the operations on a field, you need to declare you own operation type.
+Given you want to only allow `eq` and `neq` on a string field, this could look like this
+
+```csharp {7}
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.BindFieldsExplicitly();
+ descriptor.Field(f => f.Name).Type();
+ }
+}
+
+public class CustomStringOperationFilterInputType : StringOperationFilterInputType
+{
+ protected override void Configure(IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.Operation(DefaultFilterOperations.Equals).Type();
+ descriptor.Operation(DefaultFilterOperations.NotEquals).Type();
+ }
+}
+```
+
+```sdl
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ name: CustomStringOperationFilterInput
+}
+
+input CustomStringOperationFilterInput {
+ and: [CustomStringOperationFilterInput!]
+ or: [CustomStringOperationFilterInput!]
+ eq: String
+ neq: String
+}
+```
+
+To apply this filter type we just have to provide it to the `UseFiltering` extension method with as the generic type argument.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseFiltering(typeof(UserFilterType))]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor.Field(f => f.GetUsers(default))
+ .Type>>();
+ .UseFiltering()
+ }
+}
+```
+
+
+
+
+⚠️ Schema-first does currently not support filtering!
+
+
+
+
+# "and" / "or" Filter
+
+There are two built in fields.
+
+- `and`: Every condition has to be valid
+- `or` : At least one condition has to be valid
+
+Example:
+
+```graphql
+query {
+ posts(
+ first: 5
+ where: {
+ or: [{ title: { contains: "Doe" } }, { title: { contains: "John" } }]
+ }
+ ) {
+ edges {
+ node {
+ id
+ title
+ }
+ }
+ }
+}
+```
+
+**⚠️ `or` does not work when you use it like this:**
+
+```graphql
+query {
+ posts(
+ first: 5
+ where: { title: { contains: "John", or: { title: { contains: "Doe" } } } }
+ ) {
+ edges {
+ node {
+ id
+ title
+ }
+ }
+ }
+}
+```
+
+In this case the filters are applied like `title.Contains("John") && title.Contains("Doe")` rather than `title.Contains("John") || title.Contains("Doe")` how you probably intended it.
+
+## Removing "and" / "or"
+
+If you do not want to expose `and` and `or` you can remove these fields with the descriptor API:
+
+```csharp
+public class UserFilterType : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.AllowAnd(false).AllowOr(false);
+ }
+}
+```
+
+# Filter Types
+
+## Boolean Filter
+
+Defined the filter operations of a `bool` field.
+
+```csharp
+public class User
+{
+ public bool IsOnline { get; set; }
+}
+
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input BooleanOperationFilterInput {
+ eq: Boolean
+ neq: Boolean
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ isOnline: BooleanOperationFilterInput
+}
+```
+
+## Comparable Filter
+
+Defines filters for `IComparable`s like: `bool`, `byte`, `shot`, `int`, `long`, `float`, `double` `decimal`, `Guid`, `DateTime`, `DateTimeOffset` and `TimeSpan`
+
+```csharp
+public class User
+{
+ public int LoginAttempts { get; set; }
+}
+
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input ComparableOperationInt32FilterInput {
+ eq: Int
+ neq: Int
+ in: [Int!]
+ nin: [Int!]
+ gt: Int
+ ngt: Int
+ gte: Int
+ ngte: Int
+ lt: Int
+ nlt: Int
+ lte: Int
+ nlte: Int
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ loginAttempts: ComparableOperationInt32FilterInput
+}
+```
+
+## String Filter
+
+Defines filters for `string`
+
+```csharp
+public class User
+{
+ public string Name { get; set; }
+}
+
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input StringOperationFilterInput {
+ and: [StringOperationFilterInput!]
+ or: [StringOperationFilterInput!]
+ eq: String
+ neq: String
+ contains: String
+ ncontains: String
+ in: [String]
+ nin: [String]
+ startsWith: String
+ nstartsWith: String
+ endsWith: String
+ nendsWith: String
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ name: StringOperationFilterInput
+}
+```
+
+## Enum Filter
+
+Defines filters for C# enums
+
+```csharp
+public enum Role {
+ Default,
+ Moderator,
+ Admin
+}
+
+public class User
+{
+ public Role Role { get; set; }
+}
+
+public class Query
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input RoleOperationFilterInput {
+ eq: Role
+ neq: Role
+ in: [Role!]
+ nin: [Role!]
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ kind: RoleOperationFilterInput
+}
+```
+
+## Object Filter
+
+An object filter is generated for all nested objects. The object filter can also be used to filter over database relations.
+For each nested object, filters are generated.
+
+```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
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input AddressFilterInput {
+ and: [AddressFilterInput!]
+ or: [AddressFilterInput!]
+ street: StringOperationFilterInput
+ isPrimary: BooleanOperationFilterInput
+}
+
+input BooleanOperationFilterInput {
+ eq: Boolean
+ neq: Boolean
+}
+
+input StringOperationFilterInput {
+ and: [StringOperationFilterInput!]
+ or: [StringOperationFilterInput!]
+ eq: String
+ neq: String
+ contains: String
+ ncontains: String
+ in: [String]
+ nin: [String]
+ startsWith: String
+ nstartsWith: String
+ endsWith: String
+ nendsWith: String
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ address: AddressFilterInput
+}
+```
+
+## List Filter
+
+List filters are generated for all nested enumerations.
+
+```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
+{
+ [UseFiltering]
+ public IQueryable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+
+```
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+input AddressFilterInput {
+ and: [AddressFilterInput!]
+ or: [AddressFilterInput!]
+ street: StringOperationFilterInput
+ isPrimary: BooleanOperationFilterInput
+}
+
+input BooleanOperationFilterInput {
+ eq: Boolean
+ neq: Boolean
+}
+
+input ListAddressFilterInput {
+ all: AddressFilterInput
+ none: AddressFilterInput
+ some: AddressFilterInput
+ any: Boolean
+}
+
+input ListStringOperationFilterInput {
+ all: StringOperationFilterInput
+ none: StringOperationFilterInput
+ some: StringOperationFilterInput
+ any: Boolean
+}
+
+input StringOperationFilterInput {
+ and: [StringOperationFilterInput!]
+ or: [StringOperationFilterInput!]
+ eq: String
+ neq: String
+ contains: String
+ ncontains: String
+ in: [String]
+ nin: [String]
+ startsWith: String
+ nstartsWith: String
+ endsWith: String
+ nendsWith: String
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ roles: ListStringOperationFilterInput
+ addresses: ListAddressFilterInput
+}
+```
+
+# Filter Conventions
+
+If you want to change the behavior filtering globally, you want to create a convention for your filters. The filter convention comes with a fluent interface that is close to a type descriptor.
+
+## 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 have to register your custom convention on the schema builder with `AddConvention`.
+By default a new convention is empty. To add the default behavior you have to add `AddDefaults`.
+
+```csharp
+public class CustomConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor.AddDefaults();
+ }
+}
+
+builder.Services
+ .AddGraphQLServer()
+ .AddConvention();
+// or
+builder.Services
+ .AddGraphQLServer()
+ .AddConvention(new FilterConvention(x =>
+ x.AddDefaults()))
+```
+
+Often you just want to extend the default behavior of filtering. If this is the case, you can also use `FilterConventionExtension`
+
+```csharp
+public class CustomConventionExtension : FilterConventionExtension
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ // config
+ }
+}
+
+builder.Services
+ .AddGraphQLServer()
+ .AddConvention();
+// or
+builder.Services
+ .AddGraphQLServer()
+ .AddConvention(new FilterConventionExtension(x =>
+ {
+ // config
+ }));
+```
+
+## Argument Name
+
+With the convention descriptor, you can easily change the argument name of the `FilterInputType`.
+
+**Configuration**
+
+```csharp
+descriptor.ArgumentName("example_argument_name");
+```
+
+**Result**
+
+```sdl
+type Query {
+ users(example_argument_name: UserFilter): [User]
+}
+```
+
+## Binding of FilterTypes
+
+`FilterInputType`'s can be registered like any other type on the schema.
+
+**Configuration**
+
+```csharp
+public class UserFilterInput : FilterInputType
+{
+ protected override void Configure(
+ IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.Field(x => x.Name).Description("This is the name");
+ }
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddFiltering()
+ .AddType()
+ ...;
+```
+
+In case you use custom conventions, you can also bind the filter types on your convention.
+
+```csharp
+public class CustomFilterConvention : FilterConvention
+{
+ protected override void Configure(IFilterConventionDescriptor descriptor)
+ {
+ descriptor.BindRuntimeType()
+ }
+}
+
+```
+
+**Result**
+
+```sdl
+type Query {
+ users(where: UserFilterInput): [User]
+}
+
+type User {
+ name: String!
+}
+
+input UserFilterInput {
+ and: [UserFilterInput!]
+ or: [UserFilterInput!]
+ "This is the name"
+ name: StringOperationFilterInput
+}
+
+# ... StringOperationFilterInput left out for brevity
+```
+
+**Scalars / Operation Input Types**
+
+> This is also required when you use `HotChocolate.Types.Scalars`!
+
+When you add custom scalars, you will have to create custom filter types.
+Scalars are mapped to a `FilterInputType` that defines the operations that are possible for this scalar.
+The built-in scalars are already mapped to matching filter types.
+For custom scalars, or scalars from `HotChocolate.Types.Scalars`, you have to create and bind types.
+
+```csharp
+public class EmailAddressOperationFilterInputType : FilterInputType
+{
+ protected override void Configure(IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.Operation(DefaultFilterOperations.Equals).Type();
+ descriptor.Operation(DefaultFilterOperations.NotEquals).Type();
+ descriptor.Operation(DefaultFilterOperations.Contains).Type();
+ descriptor.Operation(DefaultFilterOperations.NotContains).Type();
+ descriptor.Operation(DefaultFilterOperations.In).Type>();
+ descriptor.Operation(DefaultFilterOperations.NotIn).Type>();
+ descriptor.Operation(DefaultFilterOperations.StartsWith).Type();
+ descriptor.Operation(DefaultFilterOperations.NotStartsWith).Type();
+ descriptor.Operation(DefaultFilterOperations.EndsWith).Type();
+ descriptor.Operation(DefaultFilterOperations.NotEndsWith).Type();
+ }
+}
+```
+
+For comparable value types, you can use the `ComparableOperationFilterInputType` base class.
+
+```csharp
+public class UnsignedIntOperationFilterInputType
+ : ComparableOperationFilterInputType
+{
+ protected override void Configure(IFilterInputTypeDescriptor descriptor)
+ {
+ descriptor.Name("UnsignedIntOperationFilterInputType");
+ base.Configure(descriptor);
+ }
+}
+```
+
+These types have to be registered on the filter convention:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddFiltering(x => x.AddDefaults().BindRuntimeType())
+ ...;
+```
+
+## Extend FilterTypes
+
+Instead of defining your own operation type, you can also just change the configuration of the built
+in ones.
+You can use `Configure()` to alter the configuration of a type.
+
+```csharp
+ descriptor.Configure(
+ x => x.Operation(DefaultFilterOperations.Equals).Description("Equals"))
+```
+
+```sdl
+input StringOperationFilterInput {
+ and: [StringOperationFilterInput!]
+ or: [StringOperationFilterInput!]
+ "Equals"
+ eq: String
+ neq: String
+ contains: String
+ ncontains: String
+ in: [String]
+ nin: [String]
+ startsWith: String
+ nstartsWith: String
+ endsWith: String
+ nendsWith: String
+}
+```
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/index.md b/website/src/docs/hotchocolate/v15/fetching-data/index.md
new file mode 100644
index 00000000000..459670d31c8
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/index.md
@@ -0,0 +1,49 @@
+---
+title: Overview
+---
+
+In this section we will learn everything about fetching data with Hot Chocolate.
+
+# Resolvers
+
+Resolvers are the main building blocks when it comes to fetching data. Every field in our GraphQL schema is backed by such a resolver function, responsible for returning the field's value. Since a resolver is just a function, we can use it to retrieve data from a database, a REST service, or any other data source as needed.
+
+[Learn more about resolvers](/docs/hotchocolate/v15/fetching-data/resolvers)
+
+Even though we can connect Hot Chocolate to any data source, most of the time it will be either a database or a REST service.
+
+[Learn how to fetch data from a database](/docs/hotchocolate/v15/fetching-data/fetching-from-databases)
+
+[Learn how to fetch data from a REST service](/docs/hotchocolate/v15/fetching-data/fetching-from-rest)
+
+# DataLoader
+
+DataLoaders provide a way to deduplicate and batch requests to data sources. They can significantly improve the performance of our queries and ease the load on our data sources.
+
+[Learn more about DataLoaders](/docs/hotchocolate/v15/fetching-data/dataloader)
+
+# Pagination
+
+Hot Chocolate provides pagination capabilities out of the box. They allow us to expose pagination in a standardized way and can even take care of crafting the necessary pagination queries to our databases.
+
+[Learn more about pagination](/docs/hotchocolate/v15/fetching-data/pagination)
+
+# Filtering
+
+When returning a list of entities, we often need to filter them using operations like `equals`, `contains`, `startsWith`, etc. Hot Chocolate takes away a lot of the boilerplate, by handling the generation of necessary input types and even translating the applied filters into native database queries.
+
+[Learn more about filtering](/docs/hotchocolate/v15/fetching-data/filtering)
+
+# Sorting
+
+Similar to filtering, Hot Chocolate can also autogenerate input types related to sorting. They allow us to specify by which fields and in which direction our entities should be sorted. These can also be translated into native database queries automatically.
+
+[Learn more about sorting](/docs/hotchocolate/v15/fetching-data/sorting)
+
+# Projections
+
+Projections allow Hot Chocolate to transform an incoming GraphQL query with a sub-selection of fields into an optimized database operation.
+
+For example, if the client only requests the `name` and `id` of a user in their GraphQL query, Hot Chocolate will only query the database for those two columns.
+
+[Learn more about projections](/docs/hotchocolate/v15/fetching-data/projections)
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/pagination.md b/website/src/docs/hotchocolate/v15/fetching-data/pagination.md
new file mode 100644
index 00000000000..879a7ce7dc2
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/pagination.md
@@ -0,0 +1,918 @@
+---
+title: "Pagination"
+---
+
+Pagination is one of the most common problems that we have to solve when implementing our backend. Often, sets of data are too large to pass them directly to the consumer of our service.
+
+Pagination solves this problem by giving the consumer the ability to fetch a set in chunks.
+
+# Connections
+
+_Connections_ are a standardized way to expose pagination to clients.
+
+Instead of returning a list of entries, we return a _Connection_.
+
+```sdl
+type Query {
+ users(first: Int after: String last: Int before: String): UsersConnection
+}
+
+type UsersConnection {
+ pageInfo: PageInfo!
+ edges: [UsersEdge!]
+ nodes: [User!]
+}
+
+type UsersEdge {
+ cursor: String!
+ node: User!
+}
+
+type PageInfo {
+ hasNextPage: Boolean!
+ hasPreviousPage: Boolean!
+ startCursor: String
+ endCursor: String
+}
+```
+
+You can learn more about this in the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm).
+
+> Note: _Connections_ are often associated with _cursor-based_ pagination, due to the use of a _cursor_. Nonetheless, since the specification describes the _cursor_ as opaque, it can be used to facilitate an _offset_ as well.
+
+## Definition
+
+Adding pagination capabilities to our fields is a breeze. All we have to do is add the `UsePaging` middleware.
+
+
+
+
+```csharp
+public class Query
+{
+ [UsePaging]
+ public IEnumerable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .UsePaging()
+ .Resolve(context =>
+ {
+ var repository = context.Service();
+
+ return repository.GetUsers();
+ });
+ }
+}
+```
+
+
+
+
+In the schema-first approach we define the resolver in the same way we would in the implementation-first approach.
+
+To make our life easier, we do not have to write out the _Connection_ types in our schema, we can simply return a list of our type, e.g. `[User]`. If the resolver for this field is annotated to use pagination, Hot Chocolate will automatically rewrite the field to return a proper _Connection_ type.
+
+```csharp
+public class Query
+{
+ [UsePaging]
+ public IEnumerable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+```
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddDocumentFromString(@"
+ type Query {
+ users : [User!]!
+ }
+ ")
+ .AddResolver();
+```
+
+
+
+
+For the `UsePaging` middleware to work, our resolver needs to return an `IEnumerable` or an `IQueryable`. The middleware will then apply the pagination arguments to what we have returned. In the case of an `IQueryable` this means that the pagination operations can be directly translated to native database queries.
+
+We also offer pagination integrations for some database technologies that do not use `IQueryable`.
+
+[Learn more about pagination providers](#providers)
+
+## Naming
+
+The name of the _Connection_ and Edge type is automatically inferred from the field name. If our field is called `users`, a `UsersConnection` and `UsersEdge` type is automatically generated.
+
+We can also specify a custom name for our _Connection_ like the following.
+
+
+
+
+```csharp
+public class Query
+{
+ [UsePaging(ConnectionName = "CustomUsers")]
+ public IEnumerable GetUsers(IUserRepository repository)
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .UsePaging(connectionName: "CustomUsers")
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+The strings `Connection` and `Edge` are automatically appended to this user specified value to form the names of the _Connection_ and Edge types.
+
+## Options
+
+We can define a number of options on a per-field basis.
+
+
+
+
+In the implementation-first approach we can define these options using properties on the `[UsePaging]` attribute.
+
+```csharp
+[UsePaging(MaxPageSize = 100)]
+```
+
+
+
+
+In the code-first approach we can pass an instance of `PagingOptions` to the `UsePaging` middleware.
+
+```csharp
+descriptor.Field("users").UsePaging(options: new PagingOptions
+{
+ MaxPageSize = 100
+});
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+[Learn more about the possible PagingOptions](#pagingoptions)
+
+## Changing the node type
+
+Lets say we are returning a collection of `string` from our pagination resolver, but we want these `string` to be represented in the schema using the `ID` scalar.
+
+For this we can specifically tell the `UsePaging` middleware, which type to use in the schema for representation of the returned CLR type.
+
+
+
+
+```csharp
+public class Query
+{
+ [UsePaging(typeof(IdType))]
+ public IEnumerable GetIds()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("ids")
+ .UsePaging()
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example..
+
+
+
+
+The same applies of course, if we are returning a collection of `User` from our pagination resolver, but we want to use the `UserType` for representation in the schema.
+
+## Custom pagination logic
+
+If we need more control over the pagination process we can do so, by returning a `Connection`.
+
+
+
+
+```csharp
+public class Query
+{
+ [UsePaging]
+ public Connection GetUsers(string? after, int? first, string sortBy)
+ {
+ // get users using the above arguments
+ IEnumerable users = null;
+
+ var edges = users.Select(user => new Edge(user, user.Id))
+ .ToList();
+ var pageInfo = new ConnectionPageInfo(false, false, null, null);
+
+ var connection = new Connection(edges, pageInfo,
+ ct => ValueTask.FromResult(0));
+
+ return connection;
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .UsePaging()
+ .Argument("sortBy", a => a.Type>())
+ .Resolve(context =>
+ {
+ var after = context.ArgumentValue("after");
+ var first = context.ArgumentValue("first");
+ var sortBy = context.ArgumentValue("sortBy");
+
+ // get users using the above arguments
+ IEnumerable users = null;
+
+ var edges = users.Select(user => new Edge(user, user.Id))
+ .ToList();
+ var pageInfo = new ConnectionPageInfo(false, false, null, null);
+
+ var connection = new Connection(edges, pageInfo,
+ ct => ValueTask.FromResult(0));
+
+ return connection;
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+## Adding fields to an Edge
+
+We can add new fields to an Edge type, by creating a type extension that targets the Edge type by its name.
+
+If our Edge is named `UsersEdge`, we can add a new field to it like the following.
+
+```csharp
+[ExtendObjectType("UsersEdge")]
+public class UsersEdge
+{
+ public string NewField([Parent] Edge edge)
+ {
+ var cursor = edge.Cursor;
+ var user = edge.Node;
+
+ // Omitted code for brevity
+ }
+}
+```
+
+[Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+## Adding fields to a Connection
+
+We can add new fields to a _Connection_ type, by creating a type extension that targets the _Connection_ type by its name.
+
+If our _Connection_ is named `UsersConnection`, we can add a new field to it like the following.
+
+```csharp
+[ExtendObjectType("UsersConnection")]
+public class UsersConnectionExtension
+{
+ public string NewField()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+[Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+These additional fields are great to perform aggregations either on the entire dataset, by for example issuing a second database call, or on top of the paginated result.
+
+We can access the pagination result like the following:
+
+```csharp
+[ExtendObjectType("UsersConnection")]
+public class UsersConnectionExtension
+{
+ public string NewField([Parent] Connection connection)
+ {
+ var result = connection.Edges.Sum(e => e.Node.SomeField);
+
+ // Omitted code for brevity
+ }
+}
+```
+
+> Note: If you are using [Projections](/docs/hotchocolate/v15/fetching-data/projections), be aware that some properties on your model might not be set, depending on what the user queried for.
+
+## Total count
+
+Sometimes we might want to return the total number of pageable entries.
+
+For this to work we need to enable the `IncludeTotalCount` flag on the `UsePaging` middleware.
+
+
+
+
+```csharp
+[UsePaging(IncludeTotalCount = true)]
+```
+
+
+
+
+```csharp
+descriptor.UsePaging(options: new PagingOptions
+{
+ IncludeTotalCount = true
+});
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+This will add a new field called `totalCount` to our _Connection_.
+
+```sdl
+type UsersConnection {
+ pageInfo: PageInfo!
+ edges: [UsersEdge!]
+ nodes: [User!]
+ totalCount: Int!
+}
+```
+
+If our resolver returns an `IEnumerable` or an `IQueryable` the `totalCount` will be automatically computed, if it has been specified as a subfield in the query.
+
+If we have customized our pagination and our resolver now returns a `Connection`, we have to explicitly declare how the `totalCount` value is computed.
+
+```csharp
+var connection = new Connection(
+ edges,
+ pageInfo,
+ getTotalCount: cancellationToken => ValueTask.FromResult(0));
+```
+
+# Offset Pagination
+
+> Note: While we support _offset-based_ pagination, we highly encourage the use of [_Connections_](#connections) instead. _Connections_ provide an abstraction which makes it easier to switch to another pagination mechanism later on.
+
+Besides _Connections_ we can also expose a more traditional _offset-based_ pagination.
+
+```sdl
+type Query {
+ users(skip: Int take: Int): UserCollectionSegment
+}
+
+type UserCollectionSegment {
+ items: [User!]
+ pageInfo: CollectionSegmentInfo!
+}
+
+type CollectionSegmentInfo {
+ hasNextPage: Boolean!
+ hasPreviousPage: Boolean!
+}
+```
+
+## Definition
+
+To add _offset-based_ pagination capabilities to our fields we have to add the `UseOffsetPaging` middleware.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseOffsetPaging]
+ public IEnumerable GetUsers(IUserRepository repository)
+ => repository.GetUsers();
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .UseOffsetPaging()
+ .Resolve(context =>
+ {
+ var repository = context.Service();
+
+ return repository.GetUsers();
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+For the `UseOffsetPaging` middleware to work, our resolver needs to return an `IEnumerable` or an `IQueryable`. The middleware will then apply the pagination arguments to what we have returned. In the case of an `IQueryable` this means that the pagination operations can be directly translated to native database queries.
+
+We also offer pagination integrations for some database technologies that do not use `IQueryable`.
+
+[Learn more about pagination providers](#providers)
+
+## Naming
+
+The name of the CollectionSegment type is inferred from the item type name. If our field returns a collection of `UserType` and the name of this type is `User`, the CollectionSegment will be called `UserCollectionSegment`.
+
+## Options
+
+We can define a number of options on a per-field basis.
+
+
+
+
+In the implementation-first approach we can define these options using properties on the `[UseOffsetPaging]` attribute.
+
+```csharp
+[UseOffsetPaging(MaxPageSize = 100)]
+```
+
+
+
+
+In the code-first approach we can pass an instance of `PagingOptions` to the `UseOffsetPaging` middleware.
+
+```csharp
+descriptor.Field("users").UseOffsetPaging(options: new PagingOptions
+{
+ MaxPageSize = 100
+});
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+[Learn more about the possible PagingOptions](#pagingoptions)
+
+## Changing the item type
+
+Lets say we are returning a collection of `string` from our pagination resolver, but we want these `string` to be represented in the schema using the `ID` scalar.
+
+For this we can specifically tell the `UseOffsetPaging` middleware, which type to use in the schema for representation of the returned CLR type.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseOffsetPaging(typeof(IdType))]
+ public IEnumerable GetIds()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("ids")
+ .UseOffsetPaging()
+ .Resolve(context =>
+ {
+ // Omitted code for brevity
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example..
+
+
+
+
+The same applies of course, if we are returning a collection of `User` from our pagination resolver, but we want to use the `UserType` for representation in the schema.
+
+## Custom pagination logic
+
+If we need more control over the pagination process we can do so, by returning a `CollectionSegment`.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseOffsetPaging]
+ public CollectionSegment GetUsers(int? skip, int? take, string sortBy)
+ {
+ /// get users using the above arguments
+ IEnumerable users = null;
+
+ var pageInfo = new CollectionSegmentInfo(false, false);
+
+ var collectionSegment = new CollectionSegment(
+ users,
+ pageInfo,
+ ct => ValueTask.FromResult(0));
+
+ return collectionSegment;
+ }
+}
+```
+
+
+
+
+```csharp
+public class QueryType : ObjectType
+{
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ descriptor
+ .Field("users")
+ .UseOffsetPaging()
+ .Argument("sortBy", a => a.Type>())
+ .Resolve(context =>
+ {
+ var skip = context.ArgumentValue("skip");
+ var take = context.ArgumentValue("take");
+ var sortBy = context.ArgumentValue("sortBy");
+
+ // get users using the above arguments
+ IEnumerable users = null;
+
+ var pageInfo = new CollectionSegmentInfo(false, false);
+
+ var collectionSegment = new CollectionSegment(
+ users,
+ pageInfo,
+ ct => ValueTask.FromResult(0));
+
+ return collectionSegment;
+ });
+ }
+}
+```
+
+
+
+
+Take a look at the implementation-first or code-first example..
+
+
+
+
+## Adding fields to a CollectionSegment
+
+We can add new fields to a CollectionSegment type, by creating a type extension that targets the CollectionSegment by its name.
+
+If our CollectionSegment is named `UserCollectionSegment`, we can add a new field to it like the following.
+
+```csharp
+[ExtendObjectType("UserCollectionSegment")]
+public class UserCollectionSegmentExtension
+{
+ public string NewField()
+ {
+ // Omitted code for brevity
+ }
+}
+```
+
+[Learn more about extending types](/docs/hotchocolate/v15/defining-a-schema/extending-types)
+
+These additional fields are great to perform aggregations either on the entire dataset, by for example issuing a second database call, or on top of the paginated result.
+
+We can access the pagination result like the following:
+
+```csharp
+[ExtendObjectType("UserCollectionSegment")]
+public class UserCollectionSegmentExtension
+{
+ public string NewField([Parent] CollectionSegment collectionSegment)
+ {
+ var result = collectionSegment.Items.Sum(i => i.SomeField);
+
+ // Omitted code for brevity
+ }
+}
+```
+
+> Note: If you are using [Projections](/docs/hotchocolate/v15/fetching-data/projections), be aware that some properties on your model might not be set, depending on what the user queried for.
+
+## Total count
+
+Sometimes we might want to return the total number of pageable entries.
+
+For this to work we need to enable the `IncludeTotalCount` flag on the `UseOffsetPaging` middleware.
+
+
+
+
+```csharp
+[UseOffsetPaging(IncludeTotalCount = true)]
+```
+
+
+
+
+```csharp
+descriptor.UseOffsetPaging(options: new PagingOptions
+{
+ IncludeTotalCount = true
+});
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+This will add a new field called `totalCount` to our _CollectionSegment_.
+
+```sdl
+type UserCollectionSegment {
+ pageInfo: CollectionSegmentInfo!
+ items: [User!]
+ totalCount: Int!
+}
+```
+
+If our resolver returns an `IEnumerable` or an `IQueryable` the `totalCount` will be automatically computed, if it has been specified as a subfield in the query.
+
+If we have customized our pagination and our resolver now returns a `CollectionSegment`, we have to explicitly declare how the `totalCount` value is computed.
+
+```csharp
+var collectionSegment = new CollectionSegment(
+ items,
+ pageInfo,
+ getTotalCount: cancellationToken => ValueTask.FromResult(0));
+```
+
+# Providers
+
+The `UsePaging` and `UseOffsetPaging` middleware provide a unified way of applying pagination to our resolvers. Depending on the data source used within the resolver the pagination mechanism needs to be different though. Hot Chocolate includes so called paging providers that allow us to use the same API, e.g. `UsePaging`, but for different data sources, e.g. MongoDB and SQL.
+
+Paging providers can be registered using various methods on the `IRequestExecutorBuilder`. For example the MongoDB paging provider can be registered like the following.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddMongoDbPagingProviders();
+```
+
+[Consult the specific integration documentation for more details](/docs/hotchocolate/v15/integrations)
+
+When registering paging providers we can name them to be able to explicitly reference them.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddMongoDbPagingProviders(providerName: "MongoDB");
+```
+
+They can then be referenced like the following.
+
+
+
+
+```csharp
+[UsePaging(ProviderName = "MongoDB")]
+public IEnumerable GetUsers()
+```
+
+
+
+
+```csharp
+descriptor
+ .Field("users")
+ .UsePaging(options: new PagingOptions
+ {
+ ProviderName = "MongoDB"
+ })
+```
+
+
+
+
+Take a look at the implementation-first or code-first example.
+
+
+
+
+If no `ProviderName` is specified, the correct provider is selected based on the return type of the resolver. If the provider to use can't be inferred from the return type, the first (default) provider is used automatically. If needed we can mark a paging provider as the explicit default.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .AddMongoDbPagingProviders(defaultProvider: true);
+```
+
+If no paging providers have been registered, a default paging provider capable of handling `IEnumerable` and `IQueryable` is used.
+
+# PagingOptions
+
+`PagingOptions` can either be defined on a per-field basis or [globally](#pagination-defaults).
+
+The following options can be configured.
+
+| Property | Default | Description |
+| ------------------------------ | ------- | ----------------------------------------------------------------------------------- |
+| `MaxPageSize` | `50` | Maximum number of items a client can request via `first`, `last` or `take`. |
+| `DefaultPageSize` | `10` | The default number of items, if a client does not specify`first`, `last` or `take`. |
+| `IncludeTotalCount` | `false` | Add a `totalCount` field for clients to request the total number of items. |
+| `AllowBackwardPagination` | `true` | Include `before` and `last` arguments on the _Connection_. |
+| `RequirePagingBoundaries` | `false` | Clients need to specify either `first`, `last` or `take`. |
+| `InferConnectionNameFromField` | `true` | Infer the name of the _Connection_ from the field name rather than its type. |
+| `ProviderName` | `null` | The name of the pagination provider to use. |
+
+# Pagination defaults
+
+If we want to enforce consistent pagination defaults throughout our app, we can do so by modifying the global `PagingOptions`.
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ .ModifyPagingOptions(opt => opt.MaxPageSize = 100);
+```
+
+[Learn more about possible PagingOptions](#pagingoptions)
+
+# Types of pagination
+
+In this section we will look at the most common pagination approaches and their downsides. There are mainly two concepts we find today: _offset-based_ and _cursor-based_ pagination.
+
+> Note: This section is intended as a brief overview and should not be treated as a definitive guide or recommendation.
+
+## Offset Pagination
+
+_Offset-based_ pagination is found in many server implementations whether the backend is implemented in SOAP, REST or GraphQL.
+
+It is so common, since it is the simplest form of pagination we can implement. All it requires is an `offset` (start index) and a `limit` (number of entries) argument.
+
+```sql
+SELECT * FROM Users
+ORDER BY Id
+LIMIT %limit OFFSET %offset
+```
+
+### Problems
+
+But whilst _offset-based_ pagination is simple to implement and works relatively well, there are also some problems:
+
+- Using `OFFSET` on the database-side does not scale well for large datasets. Most databases work with an index instead of numbered rows. This means the database always has to count _offset + limit_ rows, before discarding the _offset_ and only returning the requested number of rows.
+
+- If new entries are written to or removed from our database at high frequency, the _offset_ becomes unreliable, potentially skipping or returning duplicate entries.
+
+## Cursor Pagination
+
+Contrary to the _offset-based_ pagination, where we identify the position of an entry using an _offset_, _cursor-based_ pagination works by returning the pointer to the next entry in our pagination.
+
+To understand this concept better, let's look at an example: We want to paginate over the users in our application.
+
+First we execute the following to receive our first page:
+
+```sql
+SELECT * FROM Users
+ORDER BY Id
+LIMIT %limit
+```
+
+`%limit` is actually `limit + 1`. We are doing this to know wether there are more entries in our dataset and to receive the _cursor_ of the next entry (in this case its `Id`). This additional entry will not be returned to the consumer of our pagination.
+
+To now receive the second page, we execute:
+
+```sql
+SELECT * FROM Users
+WHERE Id >= %cursor
+ORDER BY Id
+LIMIT %limit
+```
+
+Using `WHERE` instead of `OFFSET` is great, since now we can leverage the index of the `Id` field and the database does not have to compute an _offset_.
+
+For this to work though, our _cursor_ needs to be **unique** and **sequential**. Most of the time the _Id_ field will be the best fit.
+
+But what if we need to sort by a field that does not have the aforementioned properties? We can simply combine the field with another field, which has the needed properties (like `Id`), to form a _cursor_.
+
+Let's look at another example: We want to paginate over the users sorted by their birthday.
+
+After receiving the first page, we create a combined _cursor_, like `"1435+2020-12-31"` (`Id` + `Birthday`), of the next entry. To receive the second page, we convert the _cursor_ to its original values (`Id` + `Birthday`) and use them in our query:
+
+```sql
+SELECT * FROM Users
+WHERE (Birthday >= %cursorBirthday
+OR (Birthday = %cursorBirthday AND Id >= %cursorId))
+ORDER BY Birthday, Id
+LIMIT %limit
+```
+
+### Problems
+
+Even though _cursor-based_ pagination can be more performant than _offset-based_ pagination, it comes with some downsides as well:
+
+- When using `WHERE` and `ORDER BY` on a field without an index, it can be slower than using `ORDER BY` with `OFFSET`.
+
+- Since we now only know of the next entry, there is no more concept of pages. If we have a feed or only _Next_ and _Previous_ buttons, this works great, but if we depend on page numbers, we are in a tight spot.
diff --git a/website/src/docs/hotchocolate/v15/fetching-data/projections.md b/website/src/docs/hotchocolate/v15/fetching-data/projections.md
new file mode 100644
index 00000000000..beb407d5d69
--- /dev/null
+++ b/website/src/docs/hotchocolate/v15/fetching-data/projections.md
@@ -0,0 +1,310 @@
+---
+title: Projections
+---
+
+Every GraphQL request specifies exactly what data should be returned. Over or under fetching can be reduced
+or even eliminated. Hot Chocolate projections leverage this concept and directly projects incoming queries
+to the database.
+
+Projections operate on `IQueryable` by default, but it is possible to create custom providers for projections
+to support a specific database driver.
+
+> ⚠️ **Note:** Projections currently need a public setter on fields they operate on in order to function correctly. Otherwise the default constructed value will be returned upon query.
+
+```graphql
+{
+ users {
+ email
+ address {
+ street
+ }
+ }
+}
+```
+
+```sql
+SELECT "u"."Email", "a"."Id" IS NOT NULL, "a"."Street"
+FROM "Users" AS "u"
+LEFT JOIN "Address" AS "a" ON "u"."AddressId" = "a"."Id"
+```
+
+# Getting Started
+
+Filtering is part of the `HotChocolate.Data` package.
+
+
+
+To use projections with your GraphQL endpoint you have to register projections on the schema:
+
+```csharp
+builder.Services
+ .AddGraphQLServer()
+ // Your schema configuration
+ .AddProjections();
+```
+
+Projections can be registered on a field. A middleware will apply the selected fields on the result.
+Support for `IQueryable` comes out of the box.
+The projection middleware will create a projection for the whole subtree of its field. Only fields that
+are members of a type will be projected. Fields that define a custom resolver cannot be projected
+to the database. If the middleware encounters a field that specifies `UseProjection()` this field will be skipped.
+
+
+
+
+```csharp
+public class Query
+{
+ [UseProjection]
+ public IQueryable