GraphQL Protocol
Learn how to configure the GraphQL protocol.
Prerequisites
The Gatling GraphQL SDK is not imported by default.
You have to manually add the following imports:
import static io.gatling.javaapi.graphql.GraphQlDsl.*;import io.gatling.javaapi.graphql.GraphQlDsl.*import io.gatling.graphql.Predef._Configure the protocol
GraphQL runs on top of the HTTP protocol, which configures the transport: base URL, headers, TLS, proxy, etc. You must register both the HTTP protocol and the GraphQL protocol.
Use the graphql object to create a GraphQL protocol.
HttpProtocolBuilder httpProtocol = http
.baseUrl("https://api.example.com")
.wsBaseUrl("wss://api.example.com");
GraphQlProtocolBuilder graphQlProtocol = graphql
// path operations are sent to (default: /graphql)
.endpoint("/graphql")
// path subscriptions open a WebSocket to (default: /graphql)
.wsEndpoint("/graphql");
// register both protocols
setUp(
scenario("GraphQL").exec(graphql.query("{ me { id } }")).injectOpen(atOnceUsers(1))
).protocols(httpProtocol, graphQlProtocol);val httpProtocol = http
.baseUrl("https://api.example.com")
.wsBaseUrl("wss://api.example.com")
val graphQlProtocol = graphql
// path operations are sent to (default: /graphql)
.endpoint("/graphql")
// path subscriptions open a WebSocket to (default: /graphql)
.wsEndpoint("/graphql")
// register both protocols
setUp(
scenario("GraphQL").exec(graphql.query("{ me { id } }")).injectOpen(atOnceUsers(1))
).protocols(httpProtocol, graphQlProtocol)val httpProtocol = http
.baseUrl("https://api.example.com")
.wsBaseUrl("wss://api.example.com")
val graphQlProtocol = graphql
// path operations are sent to (default: /graphql)
.endpoint("/graphql")
// path subscriptions open a WebSocket to (default: /graphql)
.wsEndpoint("/graphql")
// register both protocols
setUp(
scenario("GraphQL").exec(graphql.query("{ me { id } }")).inject(atOnceUsers(1))
).protocols(httpProtocol, graphQlProtocol)Endpoints
endpoint defines the path GraphQL operations are sent to.
It is resolved against the HTTP protocol baseUrl and defaults to /graphql.
wsEndpoint defines the path subscriptions open a WebSocket to.
It is resolved against the HTTP protocol wsBaseUrl and defaults to /graphql.
Both accept a Gatling Expression Language String or a function. You can also override the endpoint on each request.
Error handling
GraphQL servers usually answer with a 200 status, even when the operation failed.
Gatling therefore inspects the errors member of the response.
// fail as soon as there's an error (default)
graphql.failOnErrors();
// only fail when there's an error and no data at all
graphql.failOnDataNull();
// don't check the errors
graphql.ignoreErrors();// fail as soon as there's an error (default)
graphql.failOnErrors()
// only fail when there's an error and no data at all
graphql.failOnDataNull()
// don't check the errors
graphql.ignoreErrors()// fail as soon as there's an error (default)
graphql.failOnErrors
// only fail when there's an error and no data at all
graphql.failOnDataNull
// don't check the errors
graphql.ignoreErrorsfailOnErrors(default): fail the request as soon as the response contains at least one error, even ifdatais partially populated.failOnDataNull: only fail the request when the response contains errors and no data at all, that is to say tolerate partial results.ignoreErrors: don’t check theerrorsmember, only the HTTP status.
The error policy also applies to the messages of subscriptions.
Operation naming
Gatling reports a request under the name <operation type> <operation name>, for example query GetUser.
Anonymous operations, such as { me { id } }, don’t have a name.
By default, Gatling names them after the name of the file they were loaded from, then after their root fields, for example query me+orders.
If it can’t infer any name, Simulation fails on start.
You can pick another strategy, or make sure you never rely on inference:
// reject anonymous operations
graphQlProtocol.requireNamedOperations();
// name anonymous operations after their file name
graphQlProtocol.inferOperationNameFromFileName();
// name anonymous operations after their root fields
graphQlProtocol.inferOperationNameFromRootFields();
// name anonymous operations after the hash of their document
graphQlProtocol.inferOperationNameFromHash();
// name anonymous operations with a custom strategy
graphQlProtocol.inferOperationName(
document -> Optional.of(document.operationType().keyword() + document.sha256())
);// reject anonymous operations
graphQlProtocol.requireNamedOperations()
// name anonymous operations after their file name
graphQlProtocol.inferOperationNameFromFileName()
// name anonymous operations after their root fields
graphQlProtocol.inferOperationNameFromRootFields()
// name anonymous operations after the hash of their document
graphQlProtocol.inferOperationNameFromHash()
// name anonymous operations with a custom strategy
graphQlProtocol.inferOperationName { document ->
Optional.of(document.operationType().keyword() + document.sha256())
}// reject anonymous operations
graphQlProtocol.requireNamedOperations
// name anonymous operations after their file name
graphql.inferredOperationNaming(GraphQlOperationNaming.FileName)
// name anonymous operations after their root fields
graphql.inferredOperationNaming(GraphQlOperationNaming.RootFields)
// name anonymous operations after the hash of their document
graphql.inferredOperationNaming(GraphQlOperationNaming.Sha256)
// combine strategies, the first one that finds a name wins
graphql.inferredOperationNaming(GraphQlOperationNaming.FileName.orElse(GraphQlOperationNaming.RootFields))
// name anonymous operations with a custom strategy
graphql.inferredOperationNaming(document => Some(document.operationType.keyword + document.sha256))requireNamedOperations: fail on anonymous operations when the Simulation is built.- File name:
queries/getUser.graphqlgivesgetUser. - Root fields:
{ me { id } orders { id } }givesme+orders. - Hash: the first characters of the SHA-256 of the document. The name is stable across runs, and identical documents share the same entry.
- Custom: a function that takes the document and returns a name, or nothing to make the Simulation fail on this operation.
You can always set the name of a given request explicitly with requestName.
Gatling refuses to report two different documents under the same name.
Queries over GET
By default, Gatling sends operations with a POST request and a JSON body.
queriesOverGet makes Gatling send queries with a GET request instead, with the query, operationName and variables as query string parameters.
This makes responses cacheable by a CDN.
graphql.queriesOverGet();graphql.queriesOverGet()graphql.queriesOverGetThis only applies to queries whose document is known when the Simulation is built.
Mutations and requests with a dynamic document are still sent with a POST request.
Automatic Persisted Queries
With Automatic Persisted Queries (APQ), a client identifies a document with its SHA-256 hash instead of sending it over and over.
// send the hash alone with POST once the server has registered it
graphql.automaticPersistedQueries();
// send the hash alone with GET, so responses can be cached by a CDN
graphql.automaticPersistedQueriesOverGet();
// share what the server knows between all virtual users
graphql.automaticPersistedQueries().sharePersistedQueries();// send the hash alone with POST once the server has registered it
graphql.automaticPersistedQueries()
// send the hash alone with GET, so responses can be cached by a CDN
graphql.automaticPersistedQueriesOverGet()
// share what the server knows between all virtual users
graphql.automaticPersistedQueries().sharePersistedQueries()// send the hash alone with POST once the server has registered it
graphql.automaticPersistedQueries
// send the hash alone with GET, so responses can be cached by a CDN
graphql.automaticPersistedQueriesOverGet
// share what the server knows between all virtual users
graphql.automaticPersistedQueries.sharePersistedQueriesWith automaticPersistedQueries, Gatling sends the hash alone as a POST request once the server is known to have registered it, and the full document otherwise.
If the server answers that it doesn’t know the hash (PersistedQueryNotFound), Gatling sends the document.
If the server answers that it doesn’t support persisted queries at all (PersistedQueryNotSupported), Gatling sends the document and stops sending hashes.
With automaticPersistedQueriesOverGet, Gatling sends the hash alone as a GET request, and falls back to a POST request with the document if the server doesn’t know the hash yet.
This makes responses cacheable by a CDN.
automaticPersistedQueriesOverGet can’t fall back to plain requests against a server that doesn’t support persisted queries at all: every request would probe, then retry.
Only use it against a server that supports persisted queries.By default, every virtual user finds out on its own what the server knows.
sharePersistedQueries makes all virtual users share this knowledge.
Use it to simulate server to server traffic, where a handful of long lived clients are behind all the requests.
Persisted queries require a document known when the Simulation is built, so they can’t be used with a dynamic document.