Skip to main content

GraphQL ExecModule

Overview

GraphQLModule executes real GraphQL-over-HTTP requests from ValkyrAI workflows. Version 2.0 replaces fabricated query, batch, and schema responses with bounded HTTPS transport, typed variables, optional named operations, validated provider responses, and secret-safe failures. It is the only discoverable generic GraphQL module; the duplicate GraphQLGenericModule is now a hidden compatibility shell.

The module supports three operation types: query, mutation, and introspection. Queries and introspection may retry a small set of transient transport failures. Mutations always run once because a timeout or provider error can occur after the remote system commits a side effect.

Authentication is resolved only from a bound IntegrationAccount. Workflow inputs cannot set Authorization, cookies, proxy credentials, or other transport-controlled headers.

Usage

  1. Identify the provider's GraphQL HTTPS endpoint and the exact operation document.
  2. For a protected endpoint, create an IntegrationAccount and choose bearer, api_key, or basic authentication. Public endpoints can use none.
  3. Bind variables as a JSON object; do not interpolate credentials or user input into the document.
  4. Set operation_name when the document contains multiple operations.
  5. Treat mutation retries as an explicit workflow decision after reconciling provider state.

Migration from GraphQLGenericModule

Saved workflows may still reference GraphQLGenericModule. That class and its Spring bean remain loadable so persisted workflows can be inspected, but execution always returns MODULE_RETIRED without network access, credential resolution, or provider output. Replace the task with GraphQLModule before rerunning it.

Legacy fieldGraphQLModule fieldMigration rule
endpointendpointUse the provider's canonical absolute HTTPS endpoint. HTTP, userinfo, and fragments are rejected.
querydocumentClassify the document explicitly with operation_type.
variablesvariablesSupply a JSON object rather than an encoded string.
operationNameoperation_nameRename to the canonical snake-case input.
legacy IntegrationAccount.apiKeygraphqlAccount plus auth_schemeBind a READY account; never copy the credential into headers or workflow inputs.

The retired output is always {"status":"error","error":{"code":"MODULE_RETIRED","retryable":false,"migrationTargets":["GraphQLModule"]}}. It never reuses the old api.gql.data or api.gql.errors workflow-state keys. Map the canonical structured outputs below instead.

Inputs

NameTypeRequiredDefaultDescription and constraints
endpointstringYesNoneAbsolute HTTPS URL with a host. Userinfo and fragments are rejected.
operation_typestringYesqueryquery, mutation, or introspection. It must match the document.
documentstringQuery or mutationNoneGraphQL document, at most 256,000 Unicode code points. Ignored for introspection.
operation_namestringNoNoneGraphQL operation name, using GraphQL identifier syntax.
variablesobjectNo{}Typed JSON variables. The complete request body is capped at 1 MiB.
headersobjectNo{}At most 20 non-secret string headers. Header values are capped at 4,096 code points and cannot contain line breaks.
auth_schemestringYesbearernone, bearer, api_key, or basic.
api_key_headerstringAPI-key authX-API-KeyValid non-reserved HTTP header name.
timeout_msintegerNo30000Total call timeout from 1,000 through 60,000 milliseconds.
safe_read_retriesintegerNo1Additional transient attempts from 0 through 2 for queries and introspection only. Mutations force this to zero.

Map-I/O workflow input can override the corresponding non-secret payload values. IntegrationAccount credentials are never accepted from input or custom headers.

Outputs

NameTypeWhen presentDescription
statusstringAlwayssuccess, partial, or error.
operationTypestringParsed operationNormalized safety class.
httpStatusintegerProvider responseHTTP response status.
dataJSON valueGraphQL returned dataProvider data, including explicit JSON null values inside the data tree.
errorsarrayGraphQL returned errorsProvider GraphQL error array.
extensionsobjectGraphQL returned extensionsOptional response extensions.
attemptsintegerAlwaysHTTP attempts consumed.
errorobjectTransport or validation failureSafe code, message, and retryable fields without provider bodies or credentials.

An HTTP 200 response with both data and a non-empty errors array is partial. Errors without data are error. A successful HTTP response containing neither data nor errors is rejected as malformed.

IntegrationAccount Requirements

AuthenticationAccount fieldsRequirement
noneNoneNo IntegrationAccount is required.
bearerapiKeyREADY account; the value is sent as Authorization: Bearer ....
api_keyapiKeyREADY account; the value is sent in api_key_header.
basicusername, passwordREADY account; the values are encoded into the Basic authorization header.

Use provider tokens with the least GraphQL scopes or object permissions needed by the document. Store them in IntegrationAccount SecureFields. Never put tokens in endpoint, document, variables, headers, examples, logs, or error-handling branches.

Configuration

The IntegrationAccount value below is a symbolic secure relationship, not plaintext configuration:

{
"version": "2.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:project-api"
},
"payloadConfig": {
"parameters": "{\"endpoint\":\"https://api.example.com/graphql\",\"auth_scheme\":\"bearer\",\"operation_type\":\"query\",\"operation_name\":\"GetProject\",\"document\":\"query GetProject($id: ID!) { project(id: $id) { id name } }\",\"variables\":{\"id\":\"project-42\"},\"safe_read_retries\":1}"
}
}

Operations

OperationRequestSide effects and retry policy
queryJSON POST containing query, optional operationName, and variablesIntended for reads. Retries only HTTP 408, 429, 500, 502, 503, and 504 or network failure, up to safe_read_retries.
mutationJSON POST containing a document whose first operation is mutationMay write external state. One attempt only; redirects and automatic retry are disabled.
introspectionJSON POST with ValkyrAI's fixed bounded __schema queryRead-only. Caller-supplied documents are ignored; transient retries follow query policy.

The module does not implement subscriptions, WebSockets, multipart upload, persisted-query negotiation, automatic pagination, batching, federation composition, client-side schema validation, or redirect following.

Errors and Failure Modes

FailureBehaviorRecovery
Invalid endpoint, document, operation type, variables, headers, or limitsFails before transport with VALIDATION_ERROR.Correct the named input.
Missing or non-READY authenticated accountFails before transport.Bind or reauthorize the appropriate IntegrationAccount.
HTTP 401 or 403Returns GRAPHQL_HTTP_401 or GRAPHQL_HTTP_403; provider body is discarded.Verify token validity, scope, object access, and auth scheme.
HTTP 408, 429, or transient 5xx on a queryRetries only within the configured bound, then returns a retryable safe error.Schedule a later read and respect provider rate limits.
HTTP or network failure on a mutationReturns a non-retryable or ambiguous-delivery error after one attempt.Inspect the provider before a deliberate new mutation.
RedirectFails closed; the Location target is not followed.Configure the canonical HTTPS endpoint explicitly.
Empty, oversized, non-object, or invalid JSON responseFails without copying provider body content into the error.Verify endpoint compatibility; response cap is 5 MiB.
GraphQL errorsPreserves the bounded GraphQL error array and maps status to partial or error.Branch on application-level errors; do not assume HTTP 200 means success.

Example

Execute a named read with typed variables:

{
"endpoint": "https://api.example.com/graphql",
"operation_type": "query",
"operation_name": "GetProject",
"document": "query GetProject($id: ID!) { project(id: $id) { id name status } }",
"variables": {
"id": "project-42"
},
"auth_scheme": "bearer",
"safe_read_retries": 1
}

Expected normalized result after a valid provider response:

{
"status": "success",
"operationType": "query",
"httpStatus": 200,
"data": {
"project": {
"id": "project-42",
"name": "Launch",
"status": "READY"
}
},
"attempts": 1
}

Notes

  • Pagination: GraphQL pagination is schema-specific. Use returned cursors in a later bounded workflow step; the generic module never assumes pageInfo semantics or automatically follows cursors.
  • Limits: documents are capped at 256,000 code points, complete requests at 1 MiB, responses at 5 MiB, headers at 20 entries, and call timeout at 60 seconds.
  • Rate limits: providers may return HTTP 429 or GraphQL errors inside HTTP 200. Only transient HTTP/network failures on reads are retried automatically.
  • Idempotency: the module is classified mixed and not globally idempotent. GraphQL has no universal idempotency standard. Supply a provider-supported idempotency key as a non-secret custom header only when the provider documents it, and still reconcile ambiguous mutations.
  • API constraints: execution uses JSON POST over GraphQL-over-HTTP. Servers requiring GET-only queries, multipart uploads, subscriptions, signed request bodies, or persisted-query handshakes need a provider-specific module.
  • Destructive behavior: mutations can create, update, publish, or delete remote objects depending on the document and account permissions. Place destructive documents behind the workflow's approval policy.
  • Privacy: GraphQL responses may contain confidential fields. Downstream mappings, EventLogs, exports, and ContentData must preserve the provider and ValkyrAI authorization boundaries.
  • Unverified boundary: deterministic tests cover request construction, bearer authentication, typed variables, fixed introspection, transient read retries, single-attempt mutations, partial GraphQL responses, validation, metadata discovery, and secret redaction. Live provider execution requires a separately authorized IntegrationAccount and is not exercised by repository tests.
  • Compatibility boundary: GraphQLGenericModule performs no provider behavior and is retained only so saved workflows can be migrated safely. A normal ValkyrAI runtime deployment is required before its live catalog entry becomes hidden.