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/controllers/authorization.md
+7-5Lines changed: 7 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,8 @@ title: Authorization
4
4
sidebar_label: Authorization
5
5
---
6
6
7
+
## Quick Examples
8
+
7
9
If you've wired up ASP.NET authorization before, you'll likely familiar with the `[Authorize]` attribute and how its used to enforce security. GraphQL ASP.NET works the same way.
8
10
9
11
```csharp
@@ -43,7 +45,7 @@ public class BakeryController : GraphController
43
45
}
44
46
```
45
47
46
-
GraphQL supports nested checks at Controller and Action levels.
48
+
This library supports nested policy and role checks at Controller and Action levels.
47
49
48
50
```csharp
49
51
// BakeryController.cs
@@ -76,9 +78,9 @@ public class BakeryController : GraphController
76
78
}
77
79
```
78
80
79
-
## GraphQL Uses IAuthorizationService
81
+
## Use of IAuthorizationService
80
82
81
-
Under the hood, GraphQL taps into the your `IServiceProvider` to obtain a reference to the `IAuthorizationService` that gets created when you configure `.AddAuthorization()`. Take a peek at the GraphQL FieldAuthorizer in the source code for the full picture.
83
+
Under the hood, GraphQL taps into your `IServiceProvider` to obtain a reference to the `IAuthorizationService` that gets created when you configure `.AddAuthorization()` for policy enforcement rules. Take a look at the [Field Authorization](https://github.com/graphql-aspnet/graphql-aspnet/blob/master/src/graphql-aspnet/Middleware/FieldSecurity/Components/FieldAuthorizationMiddleware.cs) Middleware Component for the full picture.
In the diagram above we can see that user authorization in GraphQL ASP.NET makes use of the result from [ASP.NET's security pipeline](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-3.0) but makes no attempt to interact with it. Whether you use Kerberos tokens, OAUTH2, username/password, API tokens or if you support 2-factor authentication or one-time-use passwords, GraphQL doesn't care. The entirety of your authentication and authorization pipeline is executed completely before GraphQL gets involved.
93
+
In the diagram above we can see that user authorization in GraphQL ASP.NET makes use of the result from [ASP.NET's security pipeline](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) but makes no attempt to interact with it. Whether you use Kerberos tokens, OAUTH2, username/password, API tokens or if you support 2-factor authentication or one-time-use passwords, GraphQL doesn't care. The entirety of your authentication and authorization pipeline is executed by GraphQL, no special arrangements or configuration is needed.
92
94
93
95
> GraphQL ASP.NET draws from your configured authentication/authorization solution.
94
96
@@ -98,7 +100,7 @@ Null propagation rules still apply to unauthorized fields meaning if the field c
98
100
99
101
By default, a single unauthorized result does not necessarily kill an entire query, it depends on the structure of your object graph. When a field request is terminated any down-stream child fields are discarded immediately but sibling fields or unrelated ancestors continue to execute as normal.
100
102
101
-
Since this authorization occurs "per field" and not "per controller action" its possible to define the same security chain for POCO properties. This allows you to effectively deny access, by policy, to a single property of a single instantiated object if you wish. Performing security checks for every field of data (especially in parent/child scenarios) has a performance cost though, especially for larger data sets. For most scenarios enforcing security at the controller level is sufficient.
103
+
Since this authorization occurs "per field" and not "per controller action" its possible to define the same security chain for POCO properties. This allows you to effectively deny access, by policy, to a single property of an instantiated object. Performing security checks for every field of data (especially in parent/child scenarios) has a performance cost though, especially for larger data sets. For most scenarios enforcing security at the controller level is sufficient.
Copy file name to clipboardExpand all lines: docs/controllers/batch-operations.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ sidebar_label: Batch Operations
8
8
9
9
## The N+1 Problem
10
10
11
-
There are plenty of articles on the web discussing the theory behind the N+1 problem (links below) Instead, we'll jump into an into an example to illustrate the issue when it comes to GraphQL.
11
+
There are plenty of articles on the web discussing the theory behind the N+1 problem ([links below](./batch-operations#other-resources)). Instead, we'll jump into an into an example to illustrate the issue when it comes to GraphQL.
12
12
13
13
Let's build on our example from the discussion on type extensions where we created an extension to retrieve `Cake Orders` for a **single**`Bakery`. What if we're a national chain and need to see the last 50 orders for each of our stores in a region? This seems like a reasonable thing an auditor would do so lets alter our controller to fetch all our bakeries and then let our type extension fetch the cake orders.
14
14
@@ -51,11 +51,11 @@ Well that was easy, right? Not so fast. The `bakeries` field returns a `List<Bak
51
51
52
52
This is the N+1 problem. `1 query` for the bakeries + `N queries` for the cake orders, where N is the number of bakeries first retrieved.
53
53
54
-
If we could _batch_ the cake orders request and fetch all the orders for all the bakeries at once, then assign the `Cake Orders` back to their bakeries, we'd be a lot better off. No matter the number of bakeries retrieved, we'd execute 2 queries; 1 for `bakeries` and 1 for `orders`.
54
+
If we could _batch_ the cake orders request and fetch all the orders for all the bakeries at once, then assign the `Cake Orders` back to their respective bakeries, we'd be a lot better off. No matter the number of bakeries retrieved, we'd execute 2 queries; 1 for `bakeries` and 1 for `orders`.
55
55
56
56
## Data Loaders
57
57
58
-
You'll often hear the term `Data Loaders` when reading about GraphQL implementations. Methods that load the child data being requested as a single operation before assigning to each of the parents. There is no difference with GraphQL ASP.NET. You still have to write that method. But with the ability to capture action method parameters and clever use of an `IGraphActionResult` we can combine the data load phase with the assignment phase into a single `batch operation`, at least on the surface. The aim is to make it easy to read and easier to write.
58
+
You'll often hear the term `Data Loaders` when reading about GraphQL implementations. Methods that load the child data being requested as a single operation before assigning to each of the parents. There is no difference with GraphQL ASP.NET. You still have to write that method. But with the ability to capture action method parameters and clever use of an `IGraphActionResult` we can combine the data load phase with the assignment phase into a single batch operation, at least on the surface. The aim is to make it easy to read and easier to write.
Copy file name to clipboardExpand all lines: docs/controllers/field-paths.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
---
2
2
id: field-paths
3
-
title: Field Paths
4
-
sidebar_label: Field Paths
3
+
title: Virtual Graph Types
4
+
sidebar_label: Virtual Graph Types
5
5
---
6
6
7
-
## What is Field Pathing?
7
+
## What is a Virtual Graph Type?
8
8
9
9
When we reason about ASP.NET MVC, routing comes naturally. We define a URL and perform an HTTP request to fetch data.
10
10
@@ -223,11 +223,11 @@ public class BakeryController : GraphController
223
223
}
224
224
```
225
225
226
-
Since both methods map to a field path of `[mutation]/bakery/orderDonuts` this would cause a `GraphTypeDeclarationException` to be thrown when your application starts.
226
+
From a GraphQL perspective this equivilant to trying to define a `bakery` type with two fields named `orderDonuts`. Since both methods map to a field path of `[mutation]/bakery/orderDonuts` this would cause a `GraphTypeDeclarationException` to be thrown when your application starts.
227
227
228
228
With MVC the ASP.NET runtime could inspect any combinations of parameters passed on the query string or the POST body to work out which overload to call. You might be thinking, why can't GraphQL inspect the passed input arguments and make the same determination?
229
229
230
-
In some cases it probably could. But looking at this example we run into an issue:
230
+
Putting aside that it [violates the specification](http://spec.graphql.org/October2021/#sec-Objects), in some cases it probably could. But looking at this example we run into an issue:
Copy file name to clipboardExpand all lines: docs/controllers/type-extensions.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -68,7 +68,7 @@ Well that's just plain awful. We've over complicated our bakery model and made i
68
68
69
69
## The [TypeExtension] Attribute
70
70
71
-
So what do we do? We've talked in the section on [field paths](./field-paths) about GraphQL maintaining a 1:1 mapping between a field in the graph and a method to retrieve data for it. What prevents us from creating a method to fetch a list of Cake Orders and saying, "Hey, GraphQL! When someone asks for the field `[type]/bakery/orders` call our method instead of a property getter on the `Bakery` class. As it turns out, that is exactly what a `Type Extension` does.
71
+
So what do we do? We've talked in the section on [field paths](./field-paths) about GraphQL maintaining a 1:1 mapping between a field in the graph and a method to retrieve data for it (i.e. its assigned resolver). What prevents us from creating a method to fetch a list of Cake Orders and saying, "Hey, GraphQL! When someone asks for the field `[type]/bakery/orders` call our method instead of a property getter on the `Bakery` class. As it turns out, that is exactly what a `Type Extension` does.
72
72
73
73
```csharp
74
74
// Bakery.cs
@@ -101,7 +101,7 @@ There is a lot to unpack here, so lets step through it:
101
101
- The method returns `List<CakeOrder>` as the type of data it generates.
102
102
- The method takes in a `Bakery` instance (more on that in a second) as well as an integer, with a default value of `15`, to limit the number of orders to retrieve.
103
103
104
-
Now we can query the `orders` field from anywhere a bakery is returned in the object graph and GraphQL will invoke our method instead searching for a property named`Bakery.Orders`.
104
+
Now we can query the `orders` field from anywhere a bakery is returned in the object graph and GraphQL will invoke our method instead searching for a property named`Bakery.Orders`.
105
105
106
106
```javascript
107
107
query {
@@ -119,7 +119,7 @@ query {
119
119
120
120
#### But what about the Bakery parameter?
121
121
122
-
When you declare a `type extension` it will only be invoked in context of the type being extended.
122
+
When you declare a type extension it will only be invoked in context of the type being extended.
123
123
124
124
When we return a field of data from a property, an instance of the object must to exist in order to retrieve the property value. The same is true for a `type extension` except that instead of calling a property getter on the instance we're handing the entire instance to our method and letting it figure out what it needs to do with the data to resolve the field.
Copy file name to clipboardExpand all lines: docs/types/input-objects.md
+7-9Lines changed: 7 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,7 +41,7 @@ public class Donut
41
41
42
42
```
43
43
// GraphQL Type Definition
44
-
type NewDonutModel {
44
+
input NewDonutModel {
45
45
id: Int!
46
46
name: String
47
47
type: DonutType!
@@ -116,12 +116,9 @@ type Input_Donut {
116
116
</div>
117
117
<br/>
118
118
119
-
120
-
> *Note:*`KeyValuePair<T, K>` is a commonly used struct. While it can be safely used as an `OBJECT` type, it should not be used as an `INPUT_OBJECT`. This is because, [by definition](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs), `KeyValuePair<T,K>` does not declare publicly accessible setters on the `Key` and `Value` properties. Attempting to use the struct as an `INPUT_OBJECT` will not fail, but it will also not include any fields making it mostly useless.
121
-
122
119
## Methods are Ignored
123
120
124
-
While its possible to have methods be exposed as fields on regular `OBJECT` types they are ignored for input arguments regardless of the declaration rules applied to the type.
121
+
While its possible to have methods be exposed as fields on regular `OBJECT` types they are ignored for input types regardless of the declaration rules applied to the type.
125
122
126
123
<divclass="sideBySideCode hljs">
127
124
<div>
@@ -146,9 +143,10 @@ public class Donut
146
143
</div>
147
144
<div>
148
145
149
-
```
150
-
// GraphQL Type Definition
151
-
type Input_Donut {
146
+
```ruby
147
+
# GraphQL Type
148
+
# Definition
149
+
input Input_Donut {
152
150
id:Int!
153
151
name:String
154
152
type:DonutType!
@@ -162,7 +160,7 @@ type Input_Donut {
162
160
163
161
## Working With Lists
164
162
165
-
When constructing a set of items as an input value, GraphQL will instantiate a `List<T>` and fill it with the appropriate data, be that another list, another input object or a scalar. While you can declare a regular array (e.g. `Donut[]`, `int[]` etc.) as your list structure for an input argument, graphql has to rebuild its internal list structure as an array (or nested arrays) to meet the requirements of your method. In some cases, especially with nested lists, this result in an `O(n)` increase in processing time. It is recommended to use `IEnumerable<T>` or `IList<T>` to avoid this performance bottleneck when sending a lot of items as input arguments.
163
+
When constructing a set of items as an input value, GraphQL will instantiate a `List<T>` and fill it with the appropriate data, be that another list, another input object or a scalar. While you can declare a regular array (e.g. `Donut[]`, `int[]` etc.) as your list structure for an input argument, graphql has to rebuild its internal list structure as an array (or nested arrays) to meet the requirements of your method. In some cases, especially with nested lists, this results in a linear increase in processing time. It is recommended to use `IEnumerable<T>` or `IList<T>` to avoid this performance bottleneck when sending a lot of items as input arguments.
166
164
167
165
This example shows various ways of accepting collections of data as inputs to controller actions.
Copy file name to clipboardExpand all lines: docs/types/interfaces.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,7 +54,7 @@ Most of the time GraphQL is smart enough to figure out which object types you're
54
54
55
55
## Use It To Include It
56
56
57
-
When GraphQL ASP.NET starts building a schema it will read the interfaces attached to any model classes it finds and stage them in a special holding area. However, unless an interface is actually referenced as a return value of a field, be it from an action method or a model property, it won't be added to your schema and won't be visible to introspection queries. That is to say that when you register `Donut`, unless you specifically return `IPastry` from your application, GraphQL will leave it out of the schema. This goes a long ways in preventing clutter in your schema with all the interfaces you may declare internally.
57
+
When GraphQL starts building a schema it will read the interfaces attached to any model classes it finds and stage them in a special holding area. However, unless an interface is actually referenced as a return value of a field, be it from an action method or a model property, it won't be added to your schema and won't be visible to introspection queries. That is to say that when you register `Donut`, unless you specifically return `IPastry` from your application, GraphQL will leave it out of the schema. This goes a long ways in preventing clutter in your schema with all the interfaces you may declare internally.
58
58
59
59
<divclass="sideBySideCode hljs">
60
60
<div>
@@ -92,6 +92,7 @@ type Donut {
92
92
</div>
93
93
</div>
94
94
95
+
<br/>
95
96
Of course if you call `.AddGraphType<IPastry>()` during [schema configuration](../reference/schema-configuration) GraphQL will happily publish the type even if its never used in the graph.
Copy file name to clipboardExpand all lines: docs/types/objects.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ sidebar_label: Objects
6
6
7
7
The `object graph type` is one of six fundamental types defined by GraphQL. We can think of a graph query like a tree and if [scalar values](./scalars), such as `string` and `int`, are the leafs then objects are the branches.
8
8
9
-
In GraphQL ASP.NET a C# `class` is used to identify an `object` in a schema.
9
+
In GraphQL ASP.NET a C# `class`or `struct`is used to identify an `OBJECT` type in a schema.
10
10
11
11
Here we've defined a `donut` model class. The runtime will convert it, automatically, into a graph type. If you're familiar with GraphQL's own type definition language the equivalent expression is shown to the right.
12
12
@@ -40,6 +40,7 @@ type Donut {
40
40
</div>
41
41
</div>
42
42
43
+
<br/>
43
44
\*The `DonutType` enumeration is covered in the [enum type](./enums) section.
44
45
45
46
By Default, object graph types:
@@ -211,8 +212,8 @@ GraphQL will follow a cascading model of inclusion rules. Indicating a rule on t
211
212
212
213
By Default, GraphQL won't include your class in a schema unless:
213
214
214
-
- Its referenced in a controller.
215
-
- Referenced by a graph type that is referenced in a controller.
215
+
- Its referenced in a controller OR
216
+
- Referenced by a graph type that is referenced in a controller OR
216
217
- Tagged with `[GraphType]`.
217
218
218
219
But schema configurations can override this behavior and allow GraphQL to greedily include classes that it'll never use. This can expose them in an introspection query unintentionally. You can flag a class such that auto-inclusion will be skipped unless GraphQL can determine that the object is required to fulfill a request to the schema.
@@ -257,4 +258,4 @@ The usage of `struct` types as an `OBJECT` graph type is fully supported. The sa
257
258
258
259
## Reuse as Input Objects
259
260
260
-
Both `class` and `struct` types can be used as an `INPUT_OBJECT` and an output `OBJECT` graph type. See the section on [input objects](./input-objects) for some of the key differences and requirements.
261
+
Both `class` and `struct` types can be used as an `INPUT_OBJECT` and an `OBJECT` graph type. See the section on [input objects](./input-objects) for some of the key differences and requirements.
Copy file name to clipboardExpand all lines: docs/types/unions.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -121,7 +121,7 @@ public class SaladOrBread : GraphUnionProxy
121
121
122
122
## Union Name Uniqueness
123
123
124
-
Union names must be unique in a schema. If you do declare a union in multiple action methods without a proxy, GraphQL will attempt to validate the references by name and included types. As long as all declarations are the same, that is the name and the set of included types, then there graphql will accept the union. Otherwise, a `GraphTypeDeclarationException` will be thrown at startup.
124
+
Union names must be unique in a schema. If you do declare a union in multiple action methods without a proxy, GraphQL will attempt to validate the references by name and included types. As long as all declarations are the same, that is the name and the set of included types, then graphql will accept the union. Otherwise, a `GraphTypeDeclarationException` will be thrown at startup.
125
125
126
126
## Liskov Substitutions
127
127
@@ -225,7 +225,7 @@ public class BakeryController : GraphController
225
225
```
226
226
227
227
> Note: `MapType` is not based on the resolved field value, but only on the `System.Type`. This is by design to guarantee consistency in query execution.
228
-
> If your returned type causes the query to remain indeterminate a validation error (rule 6.4.3) will be applied to the query.
228
+
> If your returned type causes the query to remain indeterminate a validation error (rule [6.4.3](https://spec.graphql.org/October2021/#sec-Value-Completion)) will be applied to the query.
229
229
230
230
The query will now interpret all `Bagels` as `Rolls` and be able to process the query correctly.
0 commit comments