Skip to content

Commit 19c4a5c

Browse files
Upgrade docusaurus | Add input unions section (#76)
* Upgraded docusaurus to 3.9 * Added new section in advanced for Input Unions * Fixed backlinks to point to input unions in various sections * Updated some language to indicate net 10 where applicable
1 parent 2a100f1 commit 19c4a5c

File tree

12 files changed

+5656
-4438
lines changed

12 files changed

+5656
-4438
lines changed

.github/workflows/deploy-website.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- uses: actions/checkout@v3
1414
- uses: actions/setup-node@v4
1515
with:
16-
node-version: 18
16+
node-version: 22
1717
cache: yarn
1818

1919
- name: Install dependencies

.github/workflows/pr-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- uses: actions/checkout@v3
1616
- uses: actions/setup-node@v4
1717
with:
18-
node-version: 18
18+
node-version: 22
1919
cache: yarn
2020

2121
- name: Install dependencies

docs/advanced/custom-scalars.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
id: custom-scalars
33
title: Custom Scalars
44
sidebar_label: Custom Scalars
5-
sidebar_position: 3
5+
sidebar_position: 4
66
---
77

88
Scalars are the most basic, fundamental unit of content in GraphQL. It is one of two leaf types (the other being [enums](../types/enums)).

docs/advanced/directives.md

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public class MyInvalidDirective : GraphDirective
111111

112112
Execution Directives are applied to query documents and executed only on the request in which they are encountered.
113113

114-
### Example: @include
114+
### Custom Example: @include
115115

116116
This is the code for the built in `@include` directive:
117117

@@ -228,9 +228,46 @@ Batch extensions work differently than standard field resolvers; they don't reso
228228

229229
Type System directives are applied to schema items and executed at start up while the schema is being created.
230230

231-
### Example: @toLower
231+
### @oneOf
232232

233-
This directive will extend the resolver of a field, as its declared **in the schema**, to turn any strings into lower case letters.
233+
*See [input unions](input-unions.md) for further details*
234+
235+
### @deprecated
236+
237+
The `@deprecated` directive is a built in type system directive provided by graphql to indicate deprecation on a field definition or enum value. Below is the code for its implementation.
238+
239+
```csharp
240+
public sealed class DeprecatedDirective : GraphDirective
241+
{
242+
// additional validation checks and locations are excluded for brevity
243+
[DirectiveLocations(DirectiveLocation.FIELD_DEFINITION)]
244+
public IGraphActionResult Execute([FromGraphQL("reason", TypeExpression = "Type!")] string reason)
245+
{
246+
if (this.DirectiveTarget is IDeprecatable deprecatable)
247+
{
248+
deprecatable.IsDeprecated = true;
249+
deprecatable.DeprecationReason = reason;
250+
}
251+
252+
return this.Ok();
253+
}
254+
}
255+
```
256+
257+
This Directive:
258+
259+
- Marks a field, input field, enum value or argument as deprecated and attaches the provided deprecation reason
260+
- Requires that a reason for deprecation be supplied
261+
- The directive is executed once per definition its applied to when the schema is created.
262+
263+
264+
:::info
265+
Deprecation reason became a required value with the Sept. '25 specification update. (Library version v1.5)
266+
:::
267+
268+
### Custom Example: @toLower
269+
270+
This custom example directive will extend the resolver of a field, as its declared **in the schema**, to turn any strings into lower case letters.
234271

235272
```csharp title="Example: ToLowerDirective.cs"
236273
public class ToLowerDirective : GraphDirective
@@ -275,38 +312,6 @@ This Directive:
275312
Notice the difference in this type system directive vs. the `@toUpper` execution directive above. Where as toUpper was declared as a PostResolver on the document part, this directive extends the primary resolver of an `IGraphField` and affects ALL queries that request this field.
276313
:::
277314

278-
### Example: @deprecated
279-
280-
The `@deprecated` directive is a built in type system directive provided by graphql to indicate deprecation on a field definition or enum value. Below is the code for its implementation.
281-
282-
```csharp
283-
public sealed class DeprecatedDirective : GraphDirective
284-
{
285-
[DirectiveLocations(DirectiveLocation.FIELD_DEFINITION | DirectiveLocation.ENUM_VALUE)]
286-
public IGraphActionResult Execute([FromGraphQL("reason")] string reason = "No longer supported")
287-
{
288-
if (this.DirectiveTarget is IGraphField field)
289-
{
290-
field.IsDeprecated = true;
291-
field.DeprecationReason = reason;
292-
}
293-
else if (this.DirectiveTarget is IEnumValue enumValue)
294-
{
295-
enumValue.IsDeprecated = true;
296-
enumValue.DeprecationReason = reason;
297-
}
298-
299-
return this.Ok();
300-
}
301-
}
302-
```
303-
304-
This Directive:
305-
306-
- Targets a FIELD_DEFINITION or ENUM_VALUE.
307-
- Marks the field or enum value as deprecated and attaches the provided deprecation reason
308-
- The directive is executed once per field definition and enum value its applied to when the schema is created.
309-
310315
### Applying Type System Directives
311316

312317
#### Using the `[ApplyDirective]` attribute
@@ -507,4 +512,4 @@ public sealed class UnRedactDirective : GraphDirective
507512
## Demo Project
508513

509514
See the [Demo Projects](../reference/demo-projects.md) page for a demonstration on creating a type system directive for extending a field resolver and an execution directives
510-
that manipulates a string field result at runtime.
515+
that manipulates a string field result at runtime.

docs/advanced/graph-action-results.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
id: graph-action-results
33
title: Action Results
44
sidebar_label: Action Results
5-
sidebar_position: 4
5+
sidebar_position: 5
66
---
77

88
## What is an Action Result?

docs/advanced/input-unions.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
id: input-unions
3+
title: Input Unions
4+
sidebar_label: Input Unions
5+
sidebar_position: 3
6+
---
7+
8+
9+
10+
Input unions (Spec § [3.10.1](https://spec.graphql.org/September2025/#sec-OneOf-Input-Objects)) are a variant on standard input objects such that only one of the defined fields may be used in a query. This can be helpful if you have an input object, such as search parameters, that gives multiple ways to search, but you want the user submitting a query to choose exactly one option to search by.
11+
12+
By definition an input union is a standard [input object](../types/input-objects.md) (a class or a struct), all rules of standard input objects apply (i.e. field names must be unique, no use of interfaces etc.). However...
13+
14+
**Input unions:**<br/>
15+
✅ MUST have no default values declared on any field.<br/>
16+
✅ MUST have all nullable fields
17+
18+
:::warning
19+
A declaration exception will be thrown and the server will fail to start if either of these rules are violated for any declared input union.
20+
:::
21+
22+
## Creating An Input Union
23+
An object can be declared as an input union in multiple ways:
24+
25+
### Using Attribution
26+
27+
Use the `[OneOf]` attribute to mark an object as ALWAYS being an input union. Any place the class or struct is referenced as an input to a method it will be handled as an input union.
28+
29+
```csharp title="Declaring an input union with the [OneOf] attribute"
30+
// highlight-next-line
31+
[OneOf]
32+
[GraphType(InputName = "SearchOptions")]
33+
public class SearchDonutParams
34+
{
35+
public string Name {get; set;}
36+
public Flavor? Flavor {get; set; } // assume flavor is an enum
37+
}
38+
```
39+
40+
```csharp title="Using an Input Union in a Controller"
41+
public class BakeryController : GraphController
42+
{
43+
[QueryRoot("findDonuts")]
44+
// highlight-next-line
45+
public List<Donut> FindDonuts(SearchDonutParams search)
46+
{
47+
/// guaranteed that only one value (name or flavor) is non-null
48+
return [];
49+
}
50+
}
51+
```
52+
53+
```graphql title="Equivilant schema type definition"
54+
## relevant graphql type generated
55+
input SearchOptions @oneOf {
56+
name: String
57+
flavor: Flavor
58+
}
59+
```
60+
61+
### Inherit from GraphInputUnion
62+
63+
Creating a class that inherits from `GraphInputUnion` works in the same way as using `[OneOf]` but adds some additional quality of life features in terms of metadata and default value handling.
64+
65+
_See below for details on using `GraphInputUnion`_
66+
67+
68+
```csharp title="Inheriting from GraphInputUnion"
69+
[GraphType(InputName "SearchParams")]
70+
// highlight-next-line
71+
public class SearchDonutParams : GraphInputUnion
72+
{
73+
public string Name {get; set;}
74+
public Flavor? Flavor {get; set; } // assume flavor is an enum
75+
}
76+
```
77+
78+
```csharp title="Using an Input Union in a Controller"
79+
public class BakeryController : GraphController
80+
{
81+
[QueryRoot("findDonuts")]
82+
// highlight-next-line
83+
public List<Donut> FindDonuts(SearchDonutParams search)
84+
{
85+
/// guaranteed that only one value (name or flavor) is non-null
86+
return [];
87+
}
88+
}
89+
```
90+
91+
```graphql title="Equivilant schema type definition"
92+
## relevant graphql type generated
93+
input SearchOptions @oneOf {
94+
name: String
95+
flavor: Flavor
96+
}
97+
```
98+
99+
## Nullable Fields
100+
The specification defines an input union as *"a special variant of Input Object where exactly one field must be set and non-null, all others being omitted."* (Spec § [3.10.1](https://spec.graphql.org/September2025/#sec-OneOf-Input-Objects)). As such, all properties declared on a class or struct that is being used as an input union must be nullable, the supplied query MUST set exactly one field to a non-null value on a query document.
101+
102+
```csharp title="Example Scenarios"
103+
104+
// 🧨 FAIL: Flavor is non-nullable. A graph declaration exception will be thrown at start up.
105+
[OneOf]
106+
public class SearchDonutParams
107+
{
108+
public string Name {get; set;}
109+
public Flavor Flavor {get; set; } // assume flavor is an enum
110+
}
111+
112+
// 🧨 FAIL: Name declares a default value. A graph declaration exception will be thrown at start up.
113+
[OneOf]
114+
public class SearchDonutParams
115+
{
116+
public SearchDonutParams
117+
{
118+
this.Name = "%";
119+
}
120+
121+
public string Name {get; set;}
122+
public Flavor? Flavor {get; set; } // assume flavor is an enum
123+
}
124+
125+
// ✅ SUCCESS
126+
[OneOf]
127+
public class SearchDonutParams
128+
{
129+
public string Name {get; set;}
130+
public Flavor? Flavor {get; set; }
131+
}
132+
```
133+
134+
## Using `GraphInputUnion`
135+
This special base type can be used to expose some additional, quality of life methods for dealing with nullability and default values.
136+
137+
```csharp
138+
public abstract class GraphInputUnion
139+
{
140+
// Will return the value, if it was supplied on the query, otherwise fallbackValue.
141+
// this method is is heavily optimized to be performant at runtime
142+
public TReturn ValueOrDefault<TValue, TReturn>(Expression<Func<TObject, TValue>> selector, TReturn fallbackValue = default);
143+
}
144+
145+
[GraphType(InputName = "SearchParams")]
146+
public class SearchDonutParams : GraphInputUnion
147+
{
148+
public string Name {get; set;}
149+
150+
public Flavor? Flavor {get; set; } // assume flavor is an enum
151+
}
152+
153+
154+
// Sample Usage
155+
public class BakeryController : GraphController
156+
{
157+
[QueryRoot("findDonuts")]
158+
public List<Donut> FindDonuts(SearchDonutParams search)
159+
{
160+
InternalSearchParams internalParams = new();
161+
internalParams.Name = search.ValueOrDefault(x => x.Name, "%");
162+
internalParams.Flavor = search.ValueOrDefault(x => x.Flavor, Flavor.All);
163+
return _service.SearchDonuts(internalParams);
164+
}
165+
}
166+
```
167+
168+
:::info
169+
The `ValueOrDefault()` method will return a type of the fallback value, NOT of the input object property. This allows you to return non-null defaults in place of nullable values that must be passed on the input object. This should greatly reduce bloat in transferring query supplied values and reasonable fallbacks when necessary. When returning non-reference types, they must have compatibility between the nullable and non-nullable versions (e.g. `int` and `int?`)
170+
:::
171+
172+
173+
## Support
174+
Input Unions are supported in *GraphQL ASP.NET version 1.6* or later

docs/advanced/multiple-schema.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
id: multi-schema-support
33
title: Multi-Schema Support
44
sidebar_label: Multi-Schema Support
5-
sidebar_position: 5
5+
sidebar_position: 6
66
---
77

88
GraphQL ASP.NET supports multiple schemas on the same server out of the box. Each schema is recognized by its concrete .NET type.

docs/quick/overview.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ Use the menus on the left to navigate through the documentation. You do not need
1010

1111
## Nuget & Installation
1212

13-
<span className="pill">.NET Standard 2.0</span> <span className="pill">.NET 8</span> <span className="pill">.NET 9</span> <br/><br/>
13+
<span className="pill">.NET 8+</span><br/><br/>
14+
1415

1516
The library is available on [nuget](https://www.nuget.org/packages/GraphQL.AspNet/) and can be added to your project via the conventional means.
1617

18+
1719
```powershell title="How to Install The Library"
1820
# Using the dotnet CLI
1921
> dotnet add package GraphQL.AspNet
@@ -30,6 +32,8 @@ The library is available on [nuget](https://www.nuget.org/packages/GraphQL.AspNe
3032
</span>
3133

3234

35+
36+
3337
## Other Helpful Pages
3438

3539
These pages may be helpful in getting started with the library:

docs/types/input-objects.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,7 @@ public class Donut
297297
298298
:::caution
299299
Enum values used for the default value of input object properties MUST also exist as values in the schema or an exception will be thrown.
300-
:::
300+
:::
301+
302+
## Input Unions / @oneOf
303+
See [Input Unions](../advanced/input-unions.md) in the advanced section for details on using the `@oneOf` directive.

docusaurus.config.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// @ts-check
22
// Note: type annotations allow type checking and IDEs autocompletion
33

4-
const lightCodeTheme = require('prism-react-renderer/themes/github');
5-
const darkCodeTheme = require('prism-react-renderer/themes/palenight');
4+
const {themes} = require('prism-react-renderer');
5+
const lightCodeTheme = themes.github;
6+
const darkCodeTheme = themes.vsDark;
67

78
/** @type {import('@docusaurus/types').Config} */
89
const config = {
@@ -11,9 +12,14 @@ const config = {
1112
url: 'https://graphql-aspnet.github.io',
1213
baseUrl: '/',
1314
onBrokenLinks: 'throw',
14-
onBrokenMarkdownLinks: 'warn',
1515
favicon: 'img/favicon.ico',
1616

17+
markdown: {
18+
hooks: {
19+
onBrokenMarkdownLinks: 'warn',
20+
},
21+
},
22+
1723

1824
// GitHub pages deployment config.
1925
// If you aren't using GitHub pages, you don't need these.

0 commit comments

Comments
 (0)