This library will generate randomized GraphQL queries from a given schema.
It can be used in a few ways:
- Engineering: If you operate a GraphQL service, you might use this library to:
- develop a static test suite of GraphQL queries
- develop a battery of queries to test the effect of performance improvements
- Scientific: Understand the characteristics of various GraphQL services
Install the library using:
npm i ibm-graphql-query-generator
Usage typically relies on the generateRandomQuery
function, which can be imported like this:
const { generateRandomQuery } = require("ibm-graphql-query-generator")
All arguments are exposed as variables. Providers can be passed to provide values for these variables. For example:
const { generateRandomQuery } = require("ibm-graphql-query-generator")
const { buildSchema, print } = require("graphql")
const schema = `
type Query {
getUser(name: String!): User
getCompany(companyName: String!): Company
}
type User {
firstName: String
lastName: String
employer: Company
friends: [User]
}
type Company {
name: String
CEO: User
employees: [User]
}
`
const configuration = {
depthProbability: 0.5,
breadthProbability: 0.5,
providerMap: {
"*__*__name": () => {
const nameList = ["Alfred", "Barbara", "Charles", "Dorothy"]
return nameList[Math.floor(Math.random() * nameList.length)]
},
"*__*__companyName": () => {
const companyNameList = [
"All Systems Go",
"Business Brothers",
"Corporate Comglomerate Company",
"Data Defenders"
]
return companyNameList[
Math.floor(Math.random() * companyNameList.length)
]
}
}
}
const { queryDocument, variableValues, seed } = generateRandomQuery(
buildSchema(schema),
configuration
)
console.log(print(queryDocument))
console.log(variableValues)
We provide sample query generators for the following APIs:
This library exposes two functions for generating random GraphQL queries:
getRandomQuery(schema: GraphQLSchema, config: Configuration)
: Produces a random query from the given schema, and considering the passed configuration.getRandomMutation(schema: GraphQLSchema, config: Configuration)
: Produces a random mutation from the given schema, and considering the passed configuration.
Functions of this library accept a configuration object with the following properties:
depthProbability
(type:number
, default:0.5
): The probability (from0
to1
) that, if existent, fields that themselves have subfields are selected at every level of the generated query. The number of so selected fields depends on thebreadthProbability
.breadthProbability
(type:number
, default:0.5
): The probability (from0
to1
) that a field (nested or flat) is selected at every level of the generated query.maxDepth
(type:number
, default:5
): The maximum depths of the query / mutation to generate. This library ensures that leave nodes do not require children fields to be selected.ignoreOptionalArguments
(type:boolean
, default:true
): If set totrue
, non-mandatory arguments will not be included in the generated query / mutation.argumentsToIgnore
(type:string[]
, default:[]
): List of argument names that should be ignored in any case. If non-null arguments are configured to be ignored, an error will be thrown.argumentsToConsider
(type:string[]
, default:[]
): List of argument names that should be considered, even if the argument is optional andignoreOptionalArguments
is set totrue
.providerMap
(type:{[varNameQuery: string]: any | ProviderFunction }
, default:{}
): Map of values or functions to provide values for the variables present in the generated query / mutation. See details below.providePlaceholders
(type:boolean
, default:false
): If enabled, instead of defaulting tonull
, placeholder values of correct type are provided for variables. Specifically, the placeholders are10
for typeInt
,10.0
for typeFloat
,true
for typeBoolean
, and"PLACEHOLDER"
for typesString
andID
, and custom scalars.considerInterfaces
(type:boolean
, default:false
): Create queries containing interfaces (by calling fields in the interfaces and/or creating fragments on objects implementing the interfaces)considerUnions
(type:boolean
, default:false
): Create queries containing unions (by creating fragments on objects of the unions)seed
(type:number
, optional): Allows the generator to produce queries deterministically based on a random number generator seed. If no seed is provided, a random seed will be provided. The seed that is used to produce the query, whether user-provided or randomly generated, will be included in the output.pickNestedQueryField
(type:boolean
, default:false
): Guarantees that the generator will pick at least one nested field.
Example:
const cfg = {
'depthProbability': 0.5,
'breadthProbability': 0.5,
'maxDepth': 5,
'ignoreOptionalArguments': true,
'argumentsToIgnore': [],
'argumentsToConsider': [],
'providerMap': {'*__*__*': null},
'considerInterfaces': false,
'considerUnions': false,
'seed': 1,
'pickNestedQueryField': false
}
Whenever a randomly generated query or mutation requires an argument, this library exposes that argument as a variable. The names of these variables reflect the type and field that the argument applies to, as well as the argument name like so:
<type>__<fieldName>__<argumentName>
Alternatively, you can match using:
<type>__<fieldName>
In this case, the provider function returns an object where multiple arguments are present.
The providerMap
contains values or value producing functions for the variables in a query.
The keys of the providerMap
are either the exact name of the variable or a wildcard where either the type
, fieldName
, and/or argumentName
are replaced by a *
. For example, the key *__*__limit
matches all variables for arguments of name limit
, no matter for what field the argument is used or in which type. If no providerMap
is passed, a default map {'*__*__*': null}
is used, which provides a null
value to all variables (Note: this can be problematic if an argument defines a non-null value).
The values of the providerMap
are either the concrete argument values, or a function that will be invoked to provide that value. A provider function will get passed a map of all already provided variable values, which allows to provide values based on previous ones. It will also get passed the argument type (as a GraphQLNamedType
) as a second argument.
This library also exposes a function matchVarName(query: string, candidates: string[]): string
that, from a given list of variable names and/or variable name queries, finds the one matching the given variable name or query.
Note that for variables with an enumeration type, this library automatically chooses one value at random.
Generating random queries or mutations may fail in some cases:
- An error is thrown if a query hits the defined
maxDepth
, but there are only fields with children to choose from. Choosing such a field but then not choosing a sub-field for it (due to themaxDepth
constraint) would result in an invalid query and thus causes this library to throw an error. - An error is thrown if there is no provider defined for a variable in the generated query.
If you use this library in a scientific publication, please cite:
- The library, as: IBM, graphql-query-generator, 2020. https://github.com/IBM/graphql-query-generator.
- The work in which we introduced it, as: Cha, Wittern, Baudart, Davis, Mandel, and Laredo. A Principled Approach to GraphQL Query Cost Analysis. ESEC/FSE 2020.