diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d20aef6e..2b0cc07f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -71,15 +71,81 @@ jobs:
with:
files: .coverage/GraphQL.Server.Transports.AspNetCore.Tests/coverage.net7.0.opencover.xml,.coverage/GraphQL.Server.Transports.AspNetCore.Tests/coverage.netcoreapp2.1.opencover.xml,.coverage/GraphQL.Server.Samples.Server.Tests/coverage.net7.0.opencover.xml
+ nativeaot:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest]
+ name: NativeAOT Sample on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Setup .NET SDK for NativeAOT
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.x
+ source-url: https://nuget.pkg.github.com/graphql-dotnet/index.json
+ env:
+ NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Publish NativeAOT sample
+ working-directory: samples/Samples.NativeAot
+ run: dotnet publish -c Release -o published
+ - name: Start NativeAOT sample in background
+ working-directory: samples/Samples.NativeAot/published
+ shell: bash
+ run: |
+ if [ "$RUNNER_OS" == "Windows" ]; then
+ ./GraphQL.Server.Samples.NativeAot.exe &
+ else
+ ./GraphQL.Server.Samples.NativeAot &
+ fi
+ - name: Wait for NativeAOT sample to spin up
+ shell: bash
+ run: |
+ # Disable exit-on-error to allow retries
+ set +e
+ for i in {1..60}; do
+ echo "Request $i to the GraphQL endpoint..."
+ response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/ || true)
+ if [ "$response" -eq 200 ]; then
+ echo "Received 200 response, NativeAOT sample is ready."
+ exit 0
+ fi
+ echo "Did not receive a 200 response, sleeping for 0.5 second..."
+ sleep 0.5
+ done
+ echo "NativeAOT sample did not spin up in time."
+ exit 1
+ - name: Run GraphQL query against NativeAOT sample
+ working-directory: samples/Samples.NativeAot
+ shell: bash
+ run: |
+ # Run a simple GraphQL query. Adjust the request as needed for your sample.
+ curl -X POST -H "Content-Type: application/json" \
+ -d @sample-request.json \
+ http://localhost:5000/graphql > nativeaot_response.json
+ - name: Print query result
+ working-directory: samples/Samples.NativeAot
+ shell: bash
+ run: cat nativeaot_response.json
+ - name: Compare query result to expected response
+ working-directory: samples/Samples.NativeAot
+ shell: bash
+ run: |
+ jq . nativeaot_response.json > actual-response.json
+ jq . sample-response.json > expected-response.json
+ diff -b actual-response.json expected-response.json
+
buildcheck:
needs:
- test
+ - nativeaot
runs-on: ubuntu-latest
if: always()
steps:
- name: Pass build check
- if: ${{ needs.test.result == 'success' }}
+ if: ${{ needs.test.result == 'success' && needs.nativeaot.result == 'success' }}
run: exit 0
- name: Fail build check
- if: ${{ needs.test.result != 'success' }}
+ if: ${{ needs.test.result != 'success' || needs.nativeaot.result != 'success' }}
run: exit 1
diff --git a/Directory.Build.props b/Directory.Build.props
index a3ef8a3c..00e0ef5d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -32,7 +32,7 @@
GraphQL.Server.$(MSBuildProjectName)
GraphQL.Server.$(MSBuildProjectName)
- [8.2.1,9.0.0)
+ [8.3.0,9.0.0)
true
<_FriendAssembliesPublicKey>PublicKey=0024000004800000940000000602000000240000525341310004000001000100352162dbf27be78fc45136884b8f324aa9f1dfc928c96c24704bf1df1a8779b2f26c760ed8321eca5b95ea6bd9bb60cd025b300f73bd1f4ae1ee6e281f85c527fa013ab5cb2c3fc7a1cbef7f9bf0c9014152e6a21f6e0ac6a371f8b45c6d7139c9119df9eeecf1cf59063545bb7c07437b1bc12be2c57d108d72d6c27176fbb8
diff --git a/GraphQL.Server.sln b/GraphQL.Server.sln
index ad78bc67..4775b9a1 100644
--- a/GraphQL.Server.sln
+++ b/GraphQL.Server.sln
@@ -122,6 +122,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Upload", "samples\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Upload.Tests", "tests\Samples.Upload.Tests\Samples.Upload.Tests.csproj", "{DE3059F4-B548-4091-BFC0-5879246A2DF9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.NativeAot", "samples\Samples.NativeAot\Samples.NativeAot.csproj", "{56042483-2E36-41DF-9DC4-71DC527A36E4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -264,6 +266,10 @@ Global
{DE3059F4-B548-4091-BFC0-5879246A2DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE3059F4-B548-4091-BFC0-5879246A2DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE3059F4-B548-4091-BFC0-5879246A2DF9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {56042483-2E36-41DF-9DC4-71DC527A36E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {56042483-2E36-41DF-9DC4-71DC527A36E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {56042483-2E36-41DF-9DC4-71DC527A36E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {56042483-2E36-41DF-9DC4-71DC527A36E4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -304,6 +310,7 @@ Global
{A204E359-05E8-4CEE-891C-4CCA6570FA52} = {BBD07745-C962-4D2D-B302-6DA1BCC2FF43}
{33E2CDF5-F854-4F1A-80D5-DBF0BDF8EEA8} = {5C07AFA3-12F2-40EA-807D-7A1EEF29012B}
{DE3059F4-B548-4091-BFC0-5879246A2DF9} = {BBD07745-C962-4D2D-B302-6DA1BCC2FF43}
+ {56042483-2E36-41DF-9DC4-71DC527A36E4} = {5C07AFA3-12F2-40EA-807D-7A1EEF29012B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3FC7FA59-E938-453C-8C4A-9D5635A9489A}
diff --git a/README.md b/README.md
index e8a27db5..1d88207c 100644
--- a/README.md
+++ b/README.md
@@ -1138,6 +1138,7 @@ typical ASP.NET Core scenarios.
| EndpointRouting | .NET 8 Minimal | Demonstrates configuring GraphQL through endpoint routing |
| Jwt | .NET 8 Minimal | Demonstrates authenticating GraphQL requests with a JWT bearer token over HTTP POST and WebSocket connections |
| MultipleSchemas | .NET 8 Minimal | Demonstrates configuring multiple schemas within a single server |
+| NativeAot | .NET 8 Slim | Demonstrates configuring GraphQL for Native AOT publishing |
| Net48 | .NET Core 2.1 / .NET 4.8 | Demonstrates configuring GraphQL on .NET 4.8 / Core 2.1 |
| Pages | .NET 8 Minimal | Demonstrates configuring GraphQL on top of a Razor Pages template |
| Upload | .NET 8 Minimal | Demonstrates uploading files via the `multipart/form-data` content type |
diff --git a/samples/Samples.NativeAot/GraphTypes/QueryType.cs b/samples/Samples.NativeAot/GraphTypes/QueryType.cs
new file mode 100644
index 00000000..a9015107
--- /dev/null
+++ b/samples/Samples.NativeAot/GraphTypes/QueryType.cs
@@ -0,0 +1,12 @@
+using GraphQL.Types;
+
+namespace GraphQL.Server.Samples.NativeAot.GraphTypes;
+
+public class QueryType : ObjectGraphType
+{
+ public QueryType()
+ {
+ Field("hello")
+ .Resolve(context => "world");
+ }
+}
diff --git a/samples/Samples.NativeAot/MySchema.cs b/samples/Samples.NativeAot/MySchema.cs
new file mode 100644
index 00000000..03205ab2
--- /dev/null
+++ b/samples/Samples.NativeAot/MySchema.cs
@@ -0,0 +1,13 @@
+using GraphQL.Server.Samples.NativeAot.GraphTypes;
+using GraphQL.Types;
+
+namespace GraphQL.Server.Samples.NativeAot;
+
+public class MySchema : Schema
+{
+ public MySchema(IServiceProvider services, QueryType queryType)
+ : base(services)
+ {
+ Query = queryType;
+ }
+}
diff --git a/samples/Samples.NativeAot/Program.cs b/samples/Samples.NativeAot/Program.cs
new file mode 100644
index 00000000..566583b6
--- /dev/null
+++ b/samples/Samples.NativeAot/Program.cs
@@ -0,0 +1,18 @@
+using GraphQL;
+using GraphQL.Server.Samples.NativeAot;
+using GraphQL.Server.Samples.NativeAot.GraphTypes;
+
+var builder = WebApplication.CreateSlimBuilder(args);
+
+builder.Services.AddGraphQL(b => b
+ .AddSchema()
+ .AddSystemTextJson());
+
+builder.Services.AddTransient();
+
+var app = builder.Build();
+
+app.UseGraphQLGraphiQL("/");
+app.UseGraphQL();
+
+app.Run();
diff --git a/samples/Samples.NativeAot/Properties/launchSettings.json b/samples/Samples.NativeAot/Properties/launchSettings.json
new file mode 100644
index 00000000..45a6cec2
--- /dev/null
+++ b/samples/Samples.NativeAot/Properties/launchSettings.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "",
+ "applicationUrl": "http://localhost:5003",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/samples/Samples.NativeAot/Samples.NativeAot.csproj b/samples/Samples.NativeAot/Samples.NativeAot.csproj
new file mode 100644
index 00000000..9f7e7b40
--- /dev/null
+++ b/samples/Samples.NativeAot/Samples.NativeAot.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/samples/Samples.NativeAot/appsettings.Development.json b/samples/Samples.NativeAot/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/samples/Samples.NativeAot/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/samples/Samples.NativeAot/appsettings.json b/samples/Samples.NativeAot/appsettings.json
new file mode 100644
index 00000000..10f68b8c
--- /dev/null
+++ b/samples/Samples.NativeAot/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/samples/Samples.NativeAot/sample-request.json b/samples/Samples.NativeAot/sample-request.json
new file mode 100644
index 00000000..767e195f
--- /dev/null
+++ b/samples/Samples.NativeAot/sample-request.json
@@ -0,0 +1,3 @@
+{
+ "query": "{ hello }"
+}
diff --git a/samples/Samples.NativeAot/sample-response.json b/samples/Samples.NativeAot/sample-response.json
new file mode 100644
index 00000000..4b3fa5b6
--- /dev/null
+++ b/samples/Samples.NativeAot/sample-response.json
@@ -0,0 +1,5 @@
+{
+ "data": {
+ "hello": "world"
+ }
+}