Public

Documentation for GraphQLClient's public interface.

Client

GraphQLClient.ClientType
Client(endpoint; headers=Dict(), introspect=true)
Client(endpoint, ws_endpoint; headers=Dict(), introspect=true)

GraphQL client. If just endpoint is provided, ws_endpoint is assumed to be the same as endpoint with "http" replaced by "ws".

By default, introspection will be performed on the client. This can be turned off by setting the introspect keyword argument to false.

Fields - Public Interface

  • endpoint::String: endpoint for queries and mutations.
  • ws_endpoint::String: endpoint for subscriptions.
  • headers::Dict: contains client specific headers.
  • introspection_complete::Bool: set to true once introspection has been performed.
  • queries::Vector{String}: list of available queries.
  • mutations::Vector{String}: list of available mutations.
  • subscriptions::Vector{String}: list of available subscriptions.

Fields - Internal Use

  • type_to_fields_map::Dict{String, Dict{String, Dict{String, Any}}}: maps GQL types to their fields.
  • query_to_type_map::Dict{String, String}: maps GQL queries, mutations and subscriptions to the type(s) of their outputs(s).
  • query_to_args_map::Dict{String, Dict{String, String}}: maps GQL queries, mutations and subscriptions to their arguments and types.
  • input_object_fields_to_type_map::Dict{String, Dict{String, String}}: maps GQL input objects to their fields/types.
  • schema::Dict{String, Any}: full schema of server.
  • introspected_types::Dict{String, DataType}: dictionary containing all introspected types.

Examples

julia> client = Client("https://countries.trevorblades.com")
GraphQLClient Client
       endpoint: https://countries.trevorblades.com
    ws_endpoint: wss://countries.trevorblades.com

julia> client = Client("https://countries.trevorblades.com", "wss://countries.trevorblades.com")
GraphQLClient Client
       endpoint: https://countries.trevorblades.com
    ws_endpoint: wss://countries.trevorblades.com
source
GraphQLClient.get_queriesFunction
get_queries(client::Client)

Returns all queries available from GraphQL server. If introspection has not been performed, will run full_introspection!(client).

source
GraphQLClient.get_mutationsFunction
get_mutations(client::Client)

Returns all mutations available from GraphQL server. If introspection has not been performed, will run full_introspection!(client).

source
GraphQLClient.get_subscriptionsFunction
get_subscriptions(client::Client)

Returns all subscriptions available from GraphQL server. If introspection has not been performed, will run full_introspection!(client).

source

Operations

GraphQLClient.queryFunction
query([client::Client], query_name::Union{Alias, AbstractString}, output_type::Type=Any; kwargs...)

Perform a query on the server, using the global client if client not supplied.

If no output_fields are supplied, all possible fields (determined by introspection of client) are returned.

The query uses the endpoint field of the client.

By default query returns a GQLReponse{Any}, where the data for an individual query can be found by gql_response.data[query_name].

Arguments

  • client::Client: GraphQL client (optional). If not supplied, global_graphql_client is used.
  • query_name::Union{Alias, AbstractString}: name of query in server.
  • output_type::Type=Any: output data type for query response object. An object of type GQLResponse{output_type} will be returned. For further information, see documentation for GQLResponse.

Keyword Arguments

  • query_args=Dict(): dictionary of query argument key value pairs - can be nested with dictionaries and vectors.
  • output_fields=String[]: output fields to be returned. Can be a string, or composed of dictionaries and vectors. If empty, query will attempt to return all fields.
  • direct_write=false: if true, the query is formed by generating a string from query_args directly, and the introspected schema is not used. Any ENUMs must be wrapped in a GQLEnum. See directly_write_query_args for more information.
  • retries=1: number of times the mutation will be attempted before erroring.
  • readtimeout=0: HTTP request timeout length. Set to 0 for no timeout.
  • throw_on_execution_error=false: set to true to throw an exception if the GraphQL server response contains errors that occurred during execution.
  • verbose=0: set to 1, 2 for extra logging.

See also: GQLResponse

source
GraphQLClient.mutateFunction
mutate([client::Client], mutation_name::Union{Alias, AbstractString}, args::AbstractDict, output_type::Type=Any; kwargs...)

Perform a mutation on the server, using the global client if client not supplied.

If no output_fields are supplied, none are returned.

By default mutate returns a GQLReponse{Any}, where the data for an individual mutation can be found by gql_response.data[mutation_name].

The mutation uses the endpoint field of the client.

Arguments

  • client::Client: GraphQL client (optional). If not supplied, global_graphql_client is used.
  • mutation_name::Union{Alias, AbstractString}: name of mutation_name.
  • args: dictionary of mutation argument key value pairs - can be nested with dictionaries and vectors.
  • output_type::Type=Any: output data type for query response object. An object of type GQLResponse{output_type} will be returned.For further information, see documentation for GQLResponse.

Keyword Arguments

  • output_fields=String[]: output fields to be returned. Can be a string, or composed of dictionaries and vectors.
  • direct_write=false: if true, the query is formed by generating a string from args directly, and the introspected schema is not used. Any ENUMs must be wrapped in a GQLEnum. See directly_write_query_args for more information.
  • retries=1: number of times the mutation will be attempted before erroring.
  • readtimeout=0: HTTP request timeout length. Set to 0 for no timeout.
  • throw_on_execution_error=false: set to true to throw an exception if the GraphQL server response contains errors that occurred during execution.

See also: GQLResponse

source
GraphQLClient.open_subscriptionFunction
open_subscription(fn::Function,
                  [client::Client],
                  subscription_name::Union{Alias, AbstractString},
                  output_type::Type=Any;
                  sub_args=Dict(),
                  output_fields=String[],
                  initfn=nothing,
                  retry=true,
                  subtimeout=0,
                  stopfn=nothing,
                  throw_on_execution_error=false)

Subscribe to subscription_name, running fn on each received result and ending the subcription when fn returns true.

By default fn receives a GQLReponse{Any}, where the data for an individual result object can be found by gql_response.data[subscription_name].

If used, initfn is called once the subscription is open.

The subscription uses the ws_endpoint field of the client.

This function is designed to be used with the do keyword.

Arguments

  • fn::Function: function to be run on each result, recieves the response from the subscription. Must return a boolean to indicate whether or not to close the subscription, withtrue` closing the subscription.
  • client::Client: GraphQL client (optional). If not supplied, global_graphql_client is used.
  • subscription_name::Union{Alias, AbstractString}: name of subscription in server.
  • output_type::Type=Any: output data type for subscription response object. An object of type GQLResponse{output_type} will be returned.For further information, see documentation for GQLResponse.

Keyword Arguments

  • sub_args=Dict(): dictionary of subscription argument key value pairs - can be nested with dictionaries and vectors.
  • output_fields=String[]: output fields to be returned. Can be a string, or composed of dictionaries and vectors.
  • initfn=nothing: optional function to be run once subscription is itialised.
  • retry=true: retry if subscription fails to open.
  • subtimeout=0: if stopfn supplied, this is the period that it is called at. If stopfn is not supplied, this is the timeout for waiting for data. The timer is reset after every subscription result is received.
  • stopfn=nothing: a function to be called every subtimeout that stops the subscription if it returns positive. The timer is reset after every subscription result is received.
  • throw_on_execution_error=false: set to true to stop an error being thrown if the GraphQL server response contains errors that occurred during execution.
  • verbose=0: set to 1, 2 for extra logging.

Examples

julia> open_subscription("subSaveUser", sub_args=Dict("role" => "SYSTEM_ADMIN")) do result
           fn(result)
       end

See also: GQLResponse

source
GraphQLClient.executeFunction
execute([client::Client], query::AbstractString, output_type::Type{T}=Any; kwargs...) where T
execute([client::Client], payload::AbstractDict, output_type::Type{T}=Any; kwargs...) where T
execute(endpoint::AbstractString, query::AbstractString, headers::AbstractDict=Dict(), output_type::Type{T}=Any; variables=Dict(), kwargs...) where T
execute(endpoint::AbstractString, payload::AbstractDict, headers::AbstractDict=Dict(), output_type::Type{T}=Any; kwargs...) where T

Executes a HTTP Post request and returns the result as a GQLResponse{T}.

This function allows for lower level querying of the GraphQL server. A Client, the global client or endpoint can be queried with a query string (and optionally variables can be supplied to the keyword argument), or with the payload directly. This payload is typically a dictionary containing the key "query" at a minimum, with variables (and other keys) begin optional.

For all methods, the content type is set to application/json unless this is set differently in client.headers./headers.

Keyword Arguments

  • variables=Dict(): dictionary of variable name to value, used to construct the payload.
  • retries=1: number of times to retry.
  • readtimeout=0: close the connection if no data is received for this many seconds. Use readtimeout = 0 to disable.
  • operation_name="": name of operation to execute. Must be supplied if more than one operation in query. Empty String is equal to no name supplied.
  • throw_on_execution_error=false: set to true to throw an exception if the GraphQL server response contains errors that occurred during execution. Otherwise, errors can be found in the error field of the return value.

See also: Client, GQLResponse

Examples

julia> client = Client("https://countries.trevorblades.com");

julia> GraphQLClient.execute(client, "query{country(code:\"BR\"){name}}")
GraphQLClient.GQLResponse{Any}
  data: Dict{String, Any}
      country: Dict{String, Any}

julia> global_graphql_client(Client("https://countries.trevorblades.com"));

julia> GraphQLClient.execute("query{country(code:\"BR\"){name}}")
GraphQLClient.GQLResponse{Any}
  data: Dict{String, Any}
      country: Dict{String, Any}

julia> GraphQLClient.execute("https://countries.trevorblades.com", "query{country(code:\"BR\"){name}}")
GraphQLClient.GQLResponse{Any}
  data: Dict{String, Any}
      country: Dict{String, Any}

julia> GraphQLClient.execute(
           "https://countries.trevorblades.com",
           Dict("query" => "query{country(code:\"BR\"){name}}")
       )
GraphQLClient.GQLResponse{Any}
  data: Dict{String, Any}
      country: Dict{String, Any}

julia> query_string = """
           query getCountries{countries{name}}
           query getLanguages{languages{name}}
       """

julia> GraphQLClient.execute(client, query_string, operation_name="getCountries")
source
GraphQLClient.GQLResponseType
GQLResponse{T}

Output format for GraphQL queries, mutations and subscriptions.

Accessing data

The data field of a GQLResponse object is a Union{Nothing, Dict{String, Union{Nothing, T}}}, where the key is the query, mutation or subscription name (or Alias) and T is specified in the output_type argument of query, mutate, open_subscription and execute. If this argument is not supplied, it will default to Any. Typically this results in combinations of dictionaries and vectors which can be accessed intuitively, following the GraphQL server schema. Alternatively, if execution of a particular query fails, the value for that query will be nothing.

It is, however, possible to provide types with a StructTypes definition and GraphQLClient will attempt to build the object from the response. This must be done carefully, however, as if the response is not as expected (for example, missing fields or unexpected nulls) then the deserialisation of the response into the struct can fail. If this occurs it should be indicated by the warnings and errors outputted by GraphQLClient.

The fields of the type to deserialise can be modified to be a Union of Nothing and their original type, as the deserialisation will input Nothing if the field is missing or null.

Comparison with GraphQL Response Specification

The GraphQL specification specifies that the response can contain data, errors and extensions fields. It is important to note that in a GQLResponse object, both data and errors fields will always be present, regardless of whether or not they are returned in the server response. This is to ensure queries can be type stable. If errors is nothing, then no errors occurred. If data is null this indicates an error occurred either during or before execution.

source
GraphQLClient.GQLEnumType
GQLEnum

When using direct_write=true in queries and mutations, ENUMs must be wrapped in this type to ensure that they are not wrapped in quotes in the query string.

See directly_write_query_args for more information and examples.

source
GraphQLClient.AliasType
Alias

Contains an alias for a GraphQL field or query. Aliases can be used in the output_fields keyword argument as well as directly instead of query, mutation and subscription names.

Examples

Using an Alias instead of the query name:

julia> client = Client("https://countries.trevorblades.com");

julia> alias = Alias("country_alias", "country");

julia> query(client, alias, query_args=Dict("code"=>"BR"), output_fields=["name"]).data
Dict{String, Any} with 1 entry:
  "country_alias" => Dict{String, Any}("name"=>"Brazil")

Using an Alias in output_fields:

julia> field_alias = Alias("country_name_alias", "name");

julia> query(client, "country", query_args=Dict("code"=>"BR"), output_fields=[field_alias]).data
Dict{String, Any} with 1 entry:
  "country" => Dict{String, Any}("country_name_alias"=>"Brazil")
source
GraphQLClient.@gql_strMacro
@gql_str(document, throw_on_error=true)

Create and optionally validate a GraphQL query string.

The string is parsed and semi-validated by GraphQLParser.jl. Validation that does not need the schema from the server is performed. For further information see the GraphQLParser documentation.

Parsing errors will always be thrown, but other validation errors can be turned off using the second argument.

An additional advantage of using this macro is that dollar signs do not need to be escaped (see example below).

Examples

General usage

julia> client = Client("https://countries.trevorblades.com");

julia> str = gql"""
query($code: ID!){
    country(
        code:$code
    ){
        name
    }
}
""";

julia> variables = Dict("code" => "BR");

julia> GraphQLClient.execute(client, str; variables=variables)
GraphQLClient.GQLResponse{Any}
  data: Dict{String, Any}
          country: Dict{String, Any}

Parsing error

julia> str = gql"""
query(code: ID!){  # no $ before variable name
    country(
        code:$code
    ){
        name
    }
}
""";

# ERROR: LoadError: ArgumentError: invalid GraphQL string at byte position 7 while parsing
#     Variable name must start with '$'
#     query(code: ID!){  # no $ before 
#           ^

Validation error

julia> str = gql"""
{
    countries{
        name
    }
}

query{  # Can't have another operation when there is an anonymous operation
    countries{
        name
    }
}
""";
# ERROR: LoadError: Validation Failed

# GraphQLParser.AnonymousOperationNotAlone
#       message: This anonymous operation must be the only defined operation.
#      location: Line 1 Column 1

Turning validation off

julia> str = @gql_str """
{
    countries{
        name
    }
}

query{  # Can't have another operation when there is an anonymous operation
    countries{
        name
    }
}
""" false
# No error
source

Type Introspection

GraphQLClient.introspect_objectFunction
introspect_object([client::Client],
                  object_name;
                  force=false,
                  reset_all=false,
                  parent_type=nothing,
                  parent_map=Dict{String, Type}(),
                  mutable=true,
                  allowed_level=2,
                  custom_scalar_types=Dict{String, DataType}())

Introspects an object and creates a Julia type.

Due to the recursion that is possible with GraphQL schemas, this introspection can be difficult. Please read this docstring carefully.

Parent Type

All introspected type, by default, will be given the parent type AbstractIntrospectedStruct. The parent type of the top level object being introspected can be set by parent_type, and the parent types of any object being introspected can be set using the parent_map dictionary. If object_type is a key in parent_map and parent_type is not nothing, the value of parent_type will take precedence.

StructTypes

AbstractIntrospectedStruct has a defined StructType of Struct for JSON serialisation. You can define StructTypes for the concrete type that is introspected by doing

StructTypes.StructType(::Type{GraphQLClient.get_introspected_object(object_name)}) = StructTypes.Struct()

Recursion

Recursion is handled by two methods:

  1. Providing the allowed_level kwarg to control how deep introspection goes.
  2. Maintaining a list of objects that are currently being introspected. No object can be instrospected twice and a type cannot be used in a type definition until it has been defined itself. Therefore if this situation occurs, the fields that need to use the not-yet-defined type are ignored. This means the order in which objects are intropsected can have an impact on the final structs.

For example consider the following objects

Country:
 - name: String
 - leader: Person

Person:
 - name: String
 - countryOfBirth: Country

If we introspected Country first, the Person object would not contain the countryOfBirth field, as it is impossible to set the type of that field to Country before it is defined itself. If we introspected Person first, the Country boject would not contain the leader field for the same reason.

Keyword Arguments

  • force=false: if false, the introspection will use already introspected types for objects if they exist. If true, any previously introspected types will be overwritten if they are introspected whilst introspecting object_name. Use with caution, as other types may rely on types that are then overwritten.
  • reset_all: delete all introspected types to start from a clean sheet.
  • parent_type=nothing: the parent type to give to the new introspected struct. See comment above.
  • parent_map=Dict{String, Type}(): dictionary to maps object names to desired parent types. If parent_type is supplied, this value will take precedence over the entry in parent_map for object_name.
  • mutable=true: boolean to set the mutability of top level and all lower level types.
  • allowed_level=2: how many levels of introspection are allowed. For example, if this is 1 then only top level fields that are objects will be in the introspected type.
  • custom_scalar_types: dictionary of custom GraphQL scalar type to Julia type. This kwarg enables custom scalar types to be introspected to the correct type.
source
GraphQLClient.initialise_introspected_structFunction
initialise_introspected_struct([client::Client], name::String)
initialise_introspected_struct([client::Client], name::SubString)
initialise_introspected_struct(T::Type)

Initialise an introspected struct with all fields set to nothing. If name of type supplied as string, this get the Type from client.introspected_types.

source
GraphQLClient.create_introspected_structFunction
create_introspected_struct([client::Client], object_name::AbstractString, fields::AbstractDict)

Creates a struct for the object specified and populates its fields with the keys and values of fields.

Examples

julia> GraphQLClient.create_introspected_struct("ResultObject",Dict(:resultId=>"MyResult", :Score => 1.0))
ResultObject
  Score : 1.0
      resultId : MyResult
source