GraphQL.AzureFunctionsProxy 11.3.8.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package GraphQL.AzureFunctionsProxy --version 11.3.8.1
NuGet\Install-Package GraphQL.AzureFunctionsProxy -Version 11.3.8.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GraphQL.AzureFunctionsProxy" Version="11.3.8.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add GraphQL.AzureFunctionsProxy --version 11.3.8.1
#r "nuget: GraphQL.AzureFunctionsProxy, 11.3.8.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install GraphQL.AzureFunctionsProxy as a Cake Addin
#addin nuget:?package=GraphQL.AzureFunctionsProxy&version=11.3.8.1

// Install GraphQL.AzureFunctionsProxy as a Cake Tool
#tool nuget:?package=GraphQL.AzureFunctionsProxy&version=11.3.8.1

GraphQL.AzureFunctionsProxy

An (Unofficial) Extension pack for using HotChocolate GraphQL framework within Azure Functions for v11.

Update Notes:

  • Added support for ?SDL download of the Schema (?SDL)
  • Added support for Functioning Playground (when configured correctly iin the AzureFunction HttpTrigger route binding & new path option).
  • Reduced the number of awaits used in the Middleware proxy for performance.
  • Maintained compatibility with v11.0.4.

Prior Release Notes:

  • Added ConfigureAwait(false) to all awaits for performance.
  • Bumped to HC v11.0.4
  • Updated to HC v11.0.1.1 due to critical fixes in HC v11.0.1 that resolve an issue in HC core that had broken Interfaces (which impacted the accompanying Star Wars Demo)
  • Updated and released Nuget update to support namespace changes in v11 rc3!
  • Updated Repo & Package names to eliminate conflicts with the core HotChocolate packages.

Note: The versioning of this package is in sync with HC v11 releases as neeed, but with a fourth incrmenting revision for this package.

Overview

This is a extension package for HotChocolate GraphQL framework to enable execution within AzureFunctions using a simple Proxy so that all original functionality of functions endpoints are unaffected.

This is Unofficial but working for most common use cases.

This also includes a working example of the StarWars Project running as an Azure Function and modified only as needed to run as expected (with v11 API)!

Buy me a Coffee ☕

I'm happy to share with the community, but if you find this useful (e.g for professional use), and are so inclinded, then I do love-me-some-coffee!

<a href="https://www.buymeacoffee.com/cajuncoding" target="_blank"> <img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"> </a>

Nuget Package (>=.netcoreapp3.1, Azure Functions v3)

To use this as-is in your project, add the GraphQL.AzureFunctionsProxy NuGet package to your project. and wire up your Starup and AzureFunction endpoint as outlined below...

Demo Site (Star Wars)

This project contains a clone of the HotChocolate GraphQL Star Wars example project (Annotation based version; Pure Code First) running as an AzureFunctions app and mildly updated to use the new v11 API.

HotChocolate has changed the Execution pipeline for v11 API in many ways, and existing AzureFunctions implementation samples don't account for various common use cases like BatchRequests, etc.

NOTES:

  1. NOTE: According to the HotChocolate team on Slack, they will provide an Official AzureFunctions middleware as part of v11 (eventually). However it will be based on the cutting edge version of Azure Functions that enable running/initializing a project exactly like a normal AspNerCore app. So this library may still help address gaps in existing Azure Function projects 😃
  2. NOTE: Moderate Testing has been done on this and we are actively using it on projects, and will update with any findings; we have not completed exhaustive testing of all HotChocolate functionality.

Goals

  • To provide a working approach to using the new v11 API until an official Middleware is provided.
  • Keep this code fully encapsulated so that switching to the official Middleware will be as painless and simple as possible (with a few design assumptions aside).
  • Keep this adaptation layer as lean and DRY as possible while also supporting as much OOTB functionality as possible.
  • Ensures that the Azure Functions paradigm and flexibility are not lost, so that all OOTB C# bindings, DI, and current Function invocation are maintained.

Implementation:

This approach uses a "Middleware Proxy" pattern whereby we provide the functionality of the existing HotChocolate HTTP middleware via a proxy class that can be injected into the Azure Function, but otherwise do not change the existing AzureFunctions invocation pipeline or the HotChocolate pipeline as it is configured in the application startup class.

This Proxy exposes an "executor" interface that can process the HttpContext in an AzureFunction. However, any pre/post logic could be added before/after the invocation of the executor proxy IGraphQLAzureFunctionsExecutorProxy.

This proxy is setup by internally configuring a Middleware Proxy that is an encapsulation of the existing HotChocolate HttpPostMiddleware & HttpGetMiddleware configured as a simple pipeline for processing POST requests first and then defaulting back to GET requests, and erroring out if neither are able to handle the request.

Key Elements:

Startup Configuration

  1. The following Middleware initializer must be added into a valid AzureFunctions Configuration 'Startup.cs'
  • All other elements of HotChocolate initialization are the same using the v11 API.
        //Finally Initialize AzureFunctions Executor Proxy here...
        services.AddAzureFunctionsGraphQL();
  • Or to enable/disable new features for Schema Download (?SDL) or Playground (DEFAULT is enabled):
        //Finally Initialize AzureFunctions Executor Proxy here...
        services.AddAzureFunctionsGraphQL((options) =>
        {
            options.AzureFunctionsRoutePath = "/api/graphql"; //Default value is already `/api/graphql`
            options.EnableSchemaDefinitionDownload = true; //Default is already Enabled (true)
            options.EnablePlaygroundWebApp = true; //Default is already Enabled (true)
            options.EnableGETRequests = true; //Default is already Enabled (true)
        });
  • Note: The namespace for this new middleware and proxy classes as needed is:
using HotChocolate.AzureFunctionsProxy

Azure Function Setup (Proxy the Requests from the Function into HC Pipeline)

  1. Dependency Inject the new IGraphQLAzureFunctionsExecutorProxy into the Function Endpoint:
using HotChocolate.AzureFunctionsProxy
using....

public class StarWarsFunctionEndpoint
{
    private readonly IGraphQLAzureFunctionsExecutorProxy _graphqlExecutorProxy;

    public StarWarsFunctionEndpoint(IGraphQLAzureFunctionsExecutorProxy graphqlExecutorProxy)
    {
        _graphqlExecutorProxy = graphqlExecutorProxy;
    }
  1. Finally, the IGraphQLAzureFunctionsExecutorProxy can be invoked in the AzureFunction invocation:
        [FunctionName(nameof(StarWarsFunctionEndpoint))]
        public async Task<IActionResult> Run(
            //NOTE: The Route must be configured to match wildcard path for the Playground to function properly.
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "graphql/{*path}")] HttpRequest req,
            ILogger logger,
            CancellationToken cancellationToken
        )
        {
            logger.LogInformation("C# GraphQL Request processing via Serverless AzureFunctions...");

            return await _graphqlExecutorProxy.ExecuteFunctionsQueryAsync(
                req.HttpContext,
                logger,
                cancellationToken
            ).ConfigureAwait(false);
        }

Enabling the Playground from AzureFunctions

  1. To enable the Playground the Azure Function must be configured properly to serve all Web Assets dynamically for various paths, and the Middleware must be told what the AzureFunction path is via the Options configuration.

    • To do this, the HttpTrigger must be configured with wildcard matching on the path so that the Function will be bound to all paths for processing (e.g. CSS, JavaScript, Manifest.json asset requests):

    • Take note of the /{*path} component of the Route binding!

        //NOTE: The Route must be configured to match wildcard path for the Playground to function properly.
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "graphql/playground/{*path}")] HttpRequest req
  1. Now it's a good idea to secure this Anonymous Playground endpoint to ensure that no data can be served from this endpoing, which helps ensure that all data requests must be sent to the actual data endpoint that can be kept secure (e.g. [HttpTrigger(AuthorizationLevel.Function...)]):
    • Note: An full example of this is configured in the StarWars-AzureFunctions project.
        [FunctionName(nameof(GraphQLPlaygroundEndpoint))]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "graphql/playground/{*path}")] HttpRequest req,
            ILogger logger,
            CancellationToken cancellationToken
        )
        {
            logger.LogInformation("C# GraphQL Request processing via Serverless AzureFunctions...");

            //SECURE this endpoint against actual Data Queries
            //  This is useful for exposing the playground anonymously, but keeping the actual GraphQL data endpoint
            //  secured with AzureFunction token security and/or other authorization approach.
            if (HttpMethods.IsPost(req.Method) || (HttpMethods.IsGet(req.Method) && !string.IsNullOrWhiteSpace(req.Query["query"])))
            {
                return new BadRequestErrorMessageResult("POST or GET GraphQL queries are invalid for the Playground endpoint.");
            }

            return await _graphqlExecutorProxy.ExecuteFunctionsQueryAsync(
                req.HttpContext,
                logger,
                cancellationToken
            ).ConfigureAwait(false);
        }
  1. Now with a valid Azure Function endpoing for our playground, that is secured so that data cannot be queried, we need to explicitly tell the AzureFunctionsProxy what the expected base url path is so that the HC Middleware will successfully serve all necessary resources/assets.
    • This is done easily by setting the AzureFunctionsRoutePath option in the configuration as follows:
    • Assuming the following then the configuration would be as follows:
      • This example assumes that you use a function HttpTrigger as defined above which allows running the GraphQL Playground client on it's own endpoint that is Anonymous;
        • This allows you keep the actual /graphql data endpoint secured with Azure Functions Token security and/or other authorization approach.
      • NOTE: The HttpTrigger Route binding for Playground MUST still use the wildcard path matching for Playground to function properly.

        // In Startup.cs . . . 

        //Finally Initialize AzureFunctions Executor Proxy here...
        services.AddAzureFunctionsGraphQL((options) =>
        {
            //When accessing the GraphQL via AzureFunctions this is the path that all Urls will be prefixed with
            //  as configured in the AzureFunction host.json combined with the HttpTrigger Route binding.
            options.AzureFunctionsRoutePath = "/api/graphql/playground";
        });
Additional Playground Usage Notes:

For Playground to function properly, with Azure Functions V2 using the proxy library, you will have to use Anonymous function security when deployed (for now?). This is becuase the HC web app does not include the original querystring values for Function token ?code=123 when it makes requests for assets so they will fail with 401 Unauthorized. Alternatively, a Chrome Plugin can be used to set the Token as a header value x-functions-key.

It works well with the ModHeader Chrome Extension. However, to eliminate that dependency, I may look into making it easier to serve the Playground from an Anonymous Function (without exposing the actual data endpoitn) and/or creating Bookmarklet/Favlet that does this without an extension in Chrome as time permits...

Disclaimers:

  • PlayGround & Schema Download Functionality:
    • There is one key reason that Playground and Schema Download works -- because the DEFAULT values for the GraphQL Options are to Enable them!
    • At this time the AzureFunctionsProxy can enablee/disable the middleware by either wiring up the Middleware or not.
    • But, the HC middleware checks for GraphQL configured options that are set at Configuration build time to see if these are enable/disabled, and that is stored on Endpoint Metadata that is not accessible (to my knowledge so far) in the Azure Function
    • This is because in Azure Functions (V2) we do not control the routing configuration at the same level of control that an Asp.Net Core application has.
    • However, since the HC defaults are to be Enabled = true then it's a non-issue!
  • Subscriptsion were disabled in the example project due to unknown supportability in a serverless environment.
    • The StarWars example uses in-memory subscriptions which are incongruent with the serverless paradigm of AzureFunctions.

Credits:

  • Initial thoughts around design were adapted from OneCyrus' Repo located here.
    • OneCyrus' example is designed around HotChocolate v10 API and the execution & configuration pipelines have changed significantly in the new v11 API.
    • This example also did not support BatchRequests, Extension values, etc...
    • Therefore, rather than manually processing request as this prior example did, this approach is different and leverages alot more OOTB code from HotChocolate.AspNetCore
  • The HotChocolate Slack channel was helpful for searching and getting some feedback to iron this out quickly.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.1 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on GraphQL.AzureFunctionsProxy:

Package Downloads
GraphQL.AzureFunctionsProxy.IsolatedProcess

This is a extension package for HotChocolate GraphQL framework to enable execution within AzureFunctions V3 with .Net 5 isolated process model (e.g. out-of-process). It enables the new HotChocolate GraphQL v12 API and provides very easy integration with Azure Functions with maximum support for out-of-the-box HotChocolate functionality -- including Schema Download & Banana Cake Pop Web IDE.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
12.5.0 28,368 1/26/2022
12.4.1 1,096 1/10/2022
12.0.0.1 1,937 9/28/2021
11.3.8.1 2,147 9/28/2021
11.0.7.1 11,875 2/2/2021
11.0.4.1 2,844 12/18/2020
11.0.1.1 951 12/6/2020
11.0.0.3 235 11/17/2020
11.0.0 289 11/6/2020

- Bumped to HC v11.3.8
- Update Nuget package for deployment of latest (last) version compatible with v11; v12 is being released today (09/28/2021)

Prior Releases Notes:
- Bumped to HC v11.0.7
- Added support for download of the Schema (?SDL)
- Added support for functioning Playground (when configured correctly in the Azure Function HttpTrigger & Route Binding)- Added ConfigureAwait(false) to all awaits for performance.
- Bumped to HC v11.0.4
- Bump to HotChocolate v11.0.1 which now resolves a bug that we helped identify with interfaces in the initial release of v11.0.0.
- Updated to support namespace changes in HotChocolate v11 rc.03 with synced version here as v11.0.0.3. Bumped HotChocolate version to v11-rc.03.
- Prior release Changed Repo & Package names to eliminate conflict risks with the core HotChocolate packages.
- Updated this package to now be stable v11 (no longer preview).  Additional testing and use has shown this to be stable and actively used in projects in preparation for v11 official release.