You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/advanced/custom-scalars.md
+11-11Lines changed: 11 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,13 +5,13 @@ sidebar_label: Custom Scalars
5
5
sidebar_position: 3
6
6
---
7
7
8
-
Scalars are the most basic, fundamental unit of content in GraphQL. It is one of two leaf types (the other being [ENUMS](../types/enums)). When a query is resolved the returned data will be a set of nested key/value pairs where every key is a string and every value is either another set of key/value pairs, an enum or a scalar.
8
+
Scalars are the most basic, fundamental unit of content in GraphQL. It is one of two leaf types (the other being [enums](../types/enums)).
9
9
10
10
Enums, being a type of their own, are very straight forward in .NET. Scalars, however; can be anything. For instance, the `Uri` scalar is represented in GraphQL by a string. On the server though, we convert it into a `System.Uri` object, with all the extra features that go along with it.
11
11
12
-
This can be done for any value that can be represented as a simple set of characters. When you create a scalar you declare its .NET type, provide a value resolver that accepts raw data from a query (a `ReadOnlySpan<char>`) and returns the completed scalar value.
12
+
This can be done for any value that can be represented as a simple set of characters. When you create a scalar you declare its .NET type, provide a value resolver that accepts raw data from a query (a `ReadOnlySpan<char>`) and returns the instantiated scalar value.
13
13
14
-
Lets say we wanted to build a scalar called `Money` that can handle both an amount and currency symbol. We might accept it in a query like this:
14
+
Lets say we wanted to build a scalar called `Money` that can handle both an amount and currency symbol (e.g. "$23.45"). We might accept it in a query like this:
15
15
16
16
```csharp title="Declaring a Money Scalar"
17
17
publicclassInventoryController : GraphController
@@ -84,8 +84,8 @@ public interface ILeafValueResolver
84
84
85
85
### IScalarGraphType Members
86
86
87
-
-`Name`: The name of this scalar in GraphQL. This is the name that will be displayed in introspection queries.
88
-
-`InternalName`: An alternative name representing the scalar in server side code. This name is commonly used in logging messages and exceptions to identify the scalar in terms of the server. Its common to use the fully qualified name, i.e. `"MyNameSpace.Money"`.
87
+
-`Name`: The unique name of this scalar. This name must be used when declaring a variable.
88
+
-`InternalName`: An alternate name representing the scalar in server side code. This name is commonly used in logging messages and exceptions to identify the scalar in terms of the server definitions. Its common to use the fully qualified name, i.e. `"MyNameSpace.Money"`.
89
89
-`Description`: The phrase that will used to describe the scalar in introspection queries.
90
90
-`Kind`: Scalars must always be declared as `TypeKind.SCALAR`.
91
91
-`Publish`: Indicates if the scalar should be published for introspection queries. Unless there is a very strong reason not to, scalars should always be published. Set this value to `true`.
@@ -95,16 +95,16 @@ public interface ILeafValueResolver
95
95
-`OtherKnownTypes`: A collection of other potential types that could be used to represent the scalar in a controller class. For instance, integers can be expressed as `int` or `int?`. Most scalars will provide an empty list (e.g. `TypeCollection.Empty`).
96
96
-`SourceResolver`: An object that implements `ILeafValueResolver` which can convert raw input data into the scalar's primary `ObjectType`.
97
97
-`Serialize(object)`: A method that converts an instance of your scalar to a leaf value that is serializable in a query response
98
-
- This method must return a `number`, `string`, `bool` or `null`.
99
-
- When converting to a number this can be any C# number value type (int, float, decimal etc.).
98
+
- This method **must** return a `number`, `string`, `bool` or `null`.
99
+
- When converting to a number this method can return any C# number value type (int, float, decimal etc.).
100
100
-`SerializeToQueryLanguage(object)`: A method that converts an instance of your scalar to a string representing it if it were declared as part of a schema language type definition.
101
101
- This method is used when generated default values for field arguments and input object fields via introspection queries.
102
102
- This method must return a value exactly as it would appear in a schema type definition For example, strings must be surrounded by quotes.
103
103
104
-
-`ValidateObject(object)`: A method used when validating data returned from a a field resolver. GraphQL will call this method and provide an object instance to determine if its acceptable and can be used in a query result.
104
+
-`ValidateObject(object)`: A method used when validating data received from a a field resolver. GraphQL will call this method and provide an object instance to determine if its acceptable and can be used in a query result.
105
105
106
106
:::note
107
-
`ValidateObject(object)` should not attempt to enforce nullability rules. In general, all scalars "could be null" depending on their usage in a schema. All scalars should return `true` for a validation result if the provided object is `null`.
107
+
`ValidateObject(object)` should not attempt to enforce nullability rules. In general, all scalars "could be null" depending on their usage in a schema. All scalars should return `true` for a validation result if the provided object is `null`. A field's type expression, enforced by graphql, will decide if null is acceptable on an individual field.
108
108
:::
109
109
110
110
### ILeafValueResolver
@@ -223,7 +223,7 @@ services.AddGraphQL();
223
223
```
224
224
225
225
:::info
226
-
Since our scalar is represented by a .NET class, if we don't pre-register it GraphQL will attempt to parse the `Money` class as an input object graph type. Once registered as a scalar, any attempt to use `Money` as an object graph type will cause an exception.
226
+
Since our scalar is represented by a .NET class, if we don't pre-register it, GraphQL will attempt to parse the `Money` class as an input object graph type. Once registered as a scalar, any attempt to use `Money` as an object graph type will cause an exception.
227
227
:::
228
228
229
229
## @specifiedBy Directive
@@ -278,7 +278,7 @@ A few points about designing your scalar:
278
278
- Scalar types should be simple and work in isolation.
279
279
- The `ReadOnlySpan<char>` provided to `ILeafValueResolver.Resolve` should be all the data needed to generate a value, there should be no need to perform side effects or fetch additional data.
280
280
- Scalar types should not track any state, depend on any stateful objects, or attempt to use any sort of dependency injection.
281
-
-`ILeafValueResolver.Resolve` must be **FAST**! Since your resolver is used to construct an initial query plan from the raw query text, it'll be called many orders of magnitude more often than any other method.
281
+
-`ILeafValueResolver.Resolve` must be **FAST**! Since your resolver is used to construct an initial query plan from the raw query text, it'll be called many orders of magnitude more often than any other method. Taking the time and performing various micro-optimizations are appropriate for this method.
Copy file name to clipboardExpand all lines: docs/advanced/graph-action-results.md
+17-10Lines changed: 17 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,8 +18,10 @@ public class BakeryController : Controller
18
18
Donutdonut=null;
19
19
// ...
20
20
21
+
// highlight-start
21
22
// return the donut and indicate success
22
-
returnthis.Ok(donut);
23
+
returnthis.Ok(donut);
24
+
// highlight-end
23
25
}
24
26
}
25
27
```
@@ -33,13 +35,15 @@ public class BakeryController : Controller
33
35
Donutdonut=null;
34
36
// ...
35
37
36
-
// return the donut directly!
38
+
// highlight-start
39
+
// return the donut directly!
37
40
returndonut;
41
+
// highlight-end
38
42
}
39
43
}
40
44
```
41
45
42
-
You can either return the data itself or some alternate `IActionResult` to tell ASP.NET how to render a response.
46
+
> You can either return the data itself or some alternate `IActionResult` to tell ASP.NET how to render a response.
43
47
44
48
Some common ASP.NET action results:
45
49
@@ -48,7 +52,7 @@ Some common ASP.NET action results:
48
52
-`this.File()`: Return status 200 and stream the file to the client.
49
53
-`this.View()`: Render a razor view and send the HTML to the client.
50
54
51
-
This works the same way in GraphQL ASP.NET. The available actions are slightly different (GraphQL won't stream files) but the usage is the same. You can even write your own action results.
55
+
This works the same way in GraphQL ASP.NET. The available actions are slightly different (e.g. GraphQL won't stream files) but the usage is the same. You can even write your own action results.
52
56
53
57
## Controller Action Results
54
58
@@ -57,10 +61,10 @@ Instead of `IActionResult` we use `IGraphActionResult` from a controller action
57
61
Built in Controller Action Methods:
58
62
59
63
-`this.Ok(fieldValue)` : Return _fieldValue_ as the resolved value of the field and indicate to the runtime that everything completed successfully.
60
-
-`this.Error(message)`: Indicates a problem. Child fields are not processed and an error message with the given text and error code is added to the response payload.
64
+
-`this.Error(message)`: Indicates a problem. The field will resolve to a `null` value automatically. Child fields are not processed and an error message with the given text and error code is added to the response payload.
61
65
-`this.StartBatch()` : Initiates the start a of a new batch. See [batch operations](../controllers/batch-operations.md) for details.
62
-
-`this.Unauthorized()`: Indicate the user is not authorized to request the field. A message telling them as such will be added to the result and no child fields will be processed. The field will be returned a `null` value automatically. This is sometimes necessary for data-level validation that can't be readily determined from an `[Authorize]` attribute or query level validation.
63
-
-`this.BadRequest()`: Commonly used in conjunction with `this.ModelState`. This result indicates the data supplied to the method is not valid for the operation. If given the model state collection an error for each validation error is rendered.
66
+
-`this.Unauthorized()`: Indicate the user is not authorized to request the field. A message telling them as such will be added to the result and no child fields will be processed. The field will resolve to a `null` value automatically. This is sometimes necessary for data-level validation that can't be readily determined from an `[Authorize]` attribute or query level validation.
67
+
-`this.BadRequest()`: Commonly used in conjunction with `this.ModelState`. This result indicates the data supplied to the method is not valid for the operation. If given a model state collection, an error for each validation error is rendered.
64
68
-`this.InternalServerError()`: Indicates an unintended error, such as an exception occurred. The supplied message will be added to the response and no child fields will be resolved.
65
69
66
70
### Directive Action Results
@@ -108,7 +112,7 @@ To create a custom result, implement `IGraphActionResult`, which defines a singl
108
112
```csharp title="IGraphActionResult.cs"
109
113
publicinterfaceIGraphActionResult
110
114
{
111
-
TaskComplete(ResolutionContextcontext);
115
+
TaskCompleteAsync(ResolutionContextcontext);
112
116
}
113
117
```
114
118
@@ -134,17 +138,20 @@ Looking at the `UnauthorizedGraphActionResult` is a great example of how to impl
The result takes in an optional error message and code, providing defaults if not supplied. Then on `Complete` it adds the message to the context and cancels its execution.
157
+
The result takes in an optional error message and code, providing defaults if not supplied. Then on `CompleteAsync` it adds the message to the context and cancels its execution.
Copy file name to clipboardExpand all lines: docs/advanced/multiple-schema.md
+28-21Lines changed: 28 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,54 +12,61 @@ GraphQL ASP.NET supports multiple schemas on the same server out of the box. Eac
12
12
To register multiple schemas you'll need to create your own class that implements `ISchema`. While it is possible to implement `ISchema` directly, if you don't require any extra functionality in your schema its easier to just inherit from the default `GraphSchema`. Updating the `Name` and `Description` is highly encouraged as the information is referenced in several different messages and can be very helpful in debugging.
13
13
14
14
```csharp title="Declaring Custom Schemas"
15
+
// highlight-next-line
15
16
publicclassEmployeeSchema : GraphSchema
16
17
{
17
18
// The schema name may be referenced in some error messages
18
19
// and log entries.
19
20
publicoverridestringName=>"Employee Schema";
20
21
21
22
// The description is publically available via introspection queries.
publicoverridestringDescription=>"Employee Related Data";
23
24
}
24
25
26
+
// highlight-next-line
25
27
publicclassCustomerSchema : GraphSchema
26
28
{
27
29
publicoverridestringName=>"Customer Schema";
30
+
publicoverridestringDescription=>"Customer Related Data";
28
31
}
29
32
```
30
33
31
-
32
34
> Implementing `ISchema` and its dependencies from scratch is not a trivial task and is beyond the scope of this documentation.
33
35
34
36
35
37
## Register Each Schema
36
38
37
39
Each schema can be registered using an overload of `.AddGraphQL()` during startup.
38
40
39
-
```csharp title="Adding A Custom Schema at Startup"
40
-
services.AddGraphQL<EmployeeSchema>();
41
-
```
41
+
By default, the query handler will attempt to register a schema to `/graphql` as its URL. You'll want to ensure that each schema has its own endpoint by updating individual routes as necessary.
The query handler will attempt to register a schema to `/graphql` as its URL by default; you'll want to ensure that each schema has its own endpoint by updating the individual routes.
60
+
varapp=builder.Build();
46
61
47
-
```csharp title="Adding Multiple Schemas"
48
-
services.AddGraphQL<EmployeeSchema>((options) =>
49
-
{
50
-
// highlight-next-line
51
-
options.QueryHandler.Route="/graphql_employees";
52
-
// add assembly or graph type references here
53
-
});
54
-
55
-
services.AddGraphQL<CustomerSchema>((options) =>
56
-
{
57
-
// highlight-next-line
58
-
options.QueryHandler.Route="/graphql_customers";
59
-
// add assembly or graph type references here
60
-
});
62
+
// highlight-next-line
63
+
app.UseGraphQL();
64
+
app.Run();
61
65
```
62
66
67
+
:::note
68
+
Each schema **must** be configured to use its own endpoint.
0 commit comments