Functions/Invoke-GraphQLQuery.ps1

function Invoke-GraphQLQuery {
    <#
    .SYNOPSIS
        Sends a query or mutation to a GraphQL endpoint.
    .DESCRIPTION
        Sends a query (read operation) or mutation (create, update, delete operation) to a GraphQL endpoint.
    .PARAMETER Query
        The GraphQL query or mutation to send to the endpoint.
    .PARAMETER OperationName
        A meaningful and explicit name for your GraphQL operation.
    .PARAMETER Variables
        Variables expressed as a hash table or JSON string for your GraphQL operation.
    .PARAMETER Headers
        Specifies the headers of the web request expressed as a hash table.
    .PARAMETER Uri
        Specifies the Uniform Resource Identifier (URI) of the GraphQL endpoint to which the GraphQL query or mutation is sent.
    .PARAMETER WebSession
        Specifies a web request session. Enter the variable name, including the dollar sign (`$`).
    .PARAMETER Raw
        Tells the function to return JSON as opposed to objects.
    .NOTES
        Query and mutation default return type is a collection of objects. To return results as JSON, use the -Raw switch.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $query = '
            query RollDice($dice: Int!, $sides: Int) {
                rollDice(numDice: $dice, numSides: $sides)
            }'
 
        $variables = '
            {
                "dice": 3,
                "sides": 6
            }'
 
        Invoke-GraphQLQuery -Query $query -Variables $variables -Uri $uri
 
        Sends a GraphQL query to the endpoint 'https://mytargetserver/v1/graphql' with variables defined in $variables as JSON.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $query = '
        query RollDice($dice: Int!, $sides: Int) {
            rollDice(numDice: $dice, numSides: $sides)
        }'
 
        $variables = @{dice=3; sides=6}
 
        Invoke-GraphQLQuery -Query $query -Variables $variables -Uri $uri
 
        Sends a GraphQL query to the endpoint 'https://mytargetserver/v1/graphql' with variables defined in $variables.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $introspectionQuery = '
            query allSchemaTypes {
                __schema {
                    types {
                        name
                        kind
                        description
                    }
                }
            }
        '
 
        Invoke-GraphQLQuery -Query $introspectionQuery -Uri $uri -Raw
 
        Sends a GraphQL introspection query to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as JSON.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $results = Invoke-GraphQLQuery -Uri $uri
 
        Sends a GraphQL introspection query using the default value for the Query parameter (as opposed to specifying it) to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as objects and assigning the results to the $results variable.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $myQuery = '
            query GetUsers {
                users {
                    created_at
                    id
                    last_seen
                    name
                }
            }
        '
 
        Invoke-GraphQLQuery -Query $myQuery -Uri $uri -Raw
 
        Sends a GraphQL query to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as JSON.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $myQuery = '
            query GetUsers {
                users {
                    created_at
                    id
                    last_seen
                    name
            }
        }
        '
 
        $result = Invoke-GraphQLQuery -Query $myQuery -Uri $uri
        $result.data.users | Format-Table
 
        Sends a GraphQL query to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as objects and navigates the hierarchy to return a table view of users.
    .EXAMPLE
        $jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MjAzOTMwMjgsIm5iZiI6MTYyMDM5MzAyNywiZXhwIjoxNjIwMzkzMzI4LCJzdWIiOiJtZUBjb21wYW55LmNvbSIsImp0aSI6ImMwZTk0ZTY0ODc4ZjRlZDFhZWM3YWYwYzViOWM2ZWI5Iiwicm9sZSI6InVzZXIifQ.HaTXDunEjmyUsHs7daLe-AxEpmq58QqqFziydm7MBic"
 
        $headers = @{Authorization="Bearer $jwt"}
 
        $uri = "https://mytargetserver/v1/graphql"
 
        $myQuery = '
            query GetUsers {
                users {
                    created_at
                    id
                    last_seen
                    name
            }
        }
        '
 
        $result = Invoke-GraphQLQuery -Query $myQuery -Headers $headers -Uri $uri
        $result.data.users | Format-Table
 
        Sends a GraphQL query using JWT for authentication to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as objects and navigates the hierarchy to return a table view of users.
    .EXAMPLE
        $uri = "https://mytargetserver/v1/graphql"
 
        $myMutation = '
            mutation MyMutation {
                insert_users_one(object: {id: "57", name: "FirstName LastName"}) {
                id
            }
        }
        '
 
        $requestHeaders = @{ "x-api-key"="aoMGY{+93dx&t!5)VMu4pI8U8T.ULO" }
 
        $jsonResult = Invoke-GraphQLQuery -Mutation $myMutation -Headers $requestHeaders -Uri $uri -Raw
 
        Sends a GraphQL mutation to the endpoint 'https://mytargetserver/v1/graphql' with the results returned as JSON.
    .EXAMPLE
        gql -q 'query { users { created_at id last_seen name } }' -u 'https://mytargetserver/v1/graphql' -
 
        Sends a GraphQL query to an endpoint with the results returned as JSON (as a one-liner using aliases).
    .LINK
        https://graphql.org/
        Format-Table
        https://docs.microsoft.com/en-us/dotnet/api/microsoft.powershell.commands.webrequestsession?view=powershellsdk-7.0.0
    #>

    [CmdletBinding()]
    [Alias("gql")]
    [OutputType([System.Management.Automation.PSCustomObject], [System.String])]
    Param
    (
        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 0)][ValidateLength(12, 1073741791)][Alias("Mutation", "q", "m")][System.String]$Query = "query introspection { __schema { types { name kind description } } }",

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 1)][ValidateLength(1, 4096)][Alias("op")][System.String]$OperationName,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 2)][ValidateNotNullOrEmpty()][Alias("v")][Object]$Variables,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 3)][Alias("h")][System.Collections.Hashtable]$Headers,

        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $false,
            Position = 4)][Alias("u")][System.Uri]$Uri,

        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $false,
            Position = 5)][Microsoft.PowerShell.Commands.WebRequestSession]$WebSession,

        [Parameter(Mandatory = $false, ParameterSetName = "JSON",
            Position = 6)][Alias("AsJson", "json", "r")][Switch]$Raw

    )
    BEGIN {
        function Compress-String([string]$InputString) {
            return ($InputString -replace '\s+', ' ').Trim()
        }
    }
    PROCESS {

        # The object that will ultimately be serialized and sent to the GraphQL endpoint:
        $jsonRequestObject = [ordered]@{ }

        # Trim all spaces and flatten $OperationName parameter value and add to $jsonRequestObject:
        if ($PSBoundParameters.ContainsKey("OperationName")) {
            $cleanedOperationInput = Compress-String -InputString $OperationName
            $jsonRequestObject.Add("operationName", $cleanedOperationInput)
        }

        # Determine if $Variables is JSON or a HashTable and add to $jsonRequestObject:
        if ($PSBoundParameters.ContainsKey("Variables")) {
            $ArgumentException = New-Object -TypeName System.ArgumentException -ArgumentList "Unable to parse incoming GraphQL variables. Please ensure that passed values are either valid JSON or of type System.Collections.HashTable."

            if ($Variables.GetType().Name -eq "Hashtable") {
                $jsonRequestObject.Add("variables", $Variables)
            }
            elseif ($Variables.GetType().Name -eq "String") {
                $variableTable = @{ }

                [bool]$isValidJson = $false
                try {
                    $deserializedVariables = $Variables | ConvertFrom-Json -ErrorAction Stop

                    $deserializedVariables.PSObject.Properties | ForEach-Object {
                        $variableTable.Add($_.Name, $_.Value)
                    }

                    $jsonRequestObject.Add("variables", $variableTable)

                    $isValidJson = $true
                }
                catch {
                    $isValidJson = $false
                }

                if (-not($isValidJson)) {
                    Write-Error -Exception $ArgumentException -ErrorAction Stop
                }
            }
            else {
                Write-Error -Exception $ArgumentException -ErrorAction Stop
            }
        }

        # Trim all spaces and flatten $Query parameter value and add to $jsonRequestObject:
        $cleanedQueryInput = Compress-String -InputString $Query
        if (($cleanedQueryInput.ToLower() -notlike "query*") -and ($cleanedQueryInput.ToLower() -notlike "mutation*") ) {
            $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList "Not a valid GraphQL query or mutation. Verify syntax and try again."
            Write-Error -Exception $ArgumentException -ErrorAction Stop
        }

        # Add $Query $jsonRequestObject:
        $jsonRequestObject.Add("query", $cleanedQueryInput)

        # Serialize $jsonRequestObject:
        [string]$jsonRequestBody = ""
        try {
            $jsonRequestBody = $jsonRequestObject | ConvertTo-Json -Depth 4 -Compress -ErrorAction Stop
        }
        catch {
            Write-Error -Exception $_.Exception -ErrorAction Stop
        }

        $params = @{Uri = $Uri
            Method      = "Post"
            Body        = $jsonRequestBody
            ContentType = "application/json"
            ErrorAction = "Stop"
        }

        if ($PSBoundParameters.ContainsKey("Headers")) {
            $params.Add("Headers", $Headers)
        }

        if ($PSBoundParameters.ContainsKey("WebSession")) {
            $params.Add("WebSession", $WebSession)
        }

        $response = $null
        try {
            $response = Invoke-RestMethod @params
        }
        catch {
            Write-Error -Exception $_.Exception -ErrorAction Stop
        }

        if ($PSBoundParameters.ContainsKey("Raw")) {
            try {
                return $($response | ConvertTo-Json -Depth 100 -ErrorAction Stop)
            }
            catch {
                Write-Error -Exception $_.Exception -ErrorAction Stop
            }
        }
        else {
            try {
                return $response
            }
            catch {
                Write-Error -Exception $_.Exception -ErrorAction Stop
            }
        }
    }
}