jala_graphql 0.5.0
jala_graphql: ^0.5.0 copied to clipboard
GraphQL link for Jala, the in-app Flutter network inspector. Operation-aware capture (name, query, variables, response) for any gql_link-based client, including graphql_flutter and ferry.
jala_graphql #
GraphQL integration for Jala, the in-app Flutter network inspector: captures
every GraphQL operation (query/mutation/subscription) sent through any
gql_link-based client — including graphql_flutter and ferry, both of
which are built on gql_link — with operation name, pretty-printed query
text, variables, the response's data/errors, and a per-payload
subscription timeline.
See the repo README for what Jala is and why (replay,
filter grammar, redaction-by-default) and the jala package
for the facade that wires this up in an app.
Install #
dependencies:
jala_graphql: ^0.5.0 # requires jala_core ^0.5.0
Attach #
Insert JalaGraphQLLink before the terminating link — the one that
actually performs the network call, e.g. HttpLink:
import 'package:gql_link/gql_link.dart';
import 'package:jala_graphql/jala_graphql.dart';
final uri = Uri.parse('https://api.example.com/graphql');
final link = Link.from([
JalaGraphQLLink(endpoint: uri),
HttpLink(uri.toString()),
]);
Both graphql_flutter's GraphQLClient and ferry's Client accept a
Link, so this same chain works for either.
Endpoint URL #
gql_link links never see the URL a downstream terminating link is
configured with — that's private to HttpLink (or whatever terminates the
chain). Pass endpoint so captured entries show the real GraphQL endpoint;
when omitted, entries fall back to a placeholder URL
(graphql://unknown-endpoint) so NetworkRequestEvent.uri — a required,
non-nullable field — always has something to show, while making clear
in the inspector that the real endpoint wasn't provided.
What gets captured #
operationName/operationType(query/mutation/subscription), shown on the list tile in place of the usual method chip and host+path title (seejala_ui).- The request body is captured as the standard GraphQL-over-HTTP shape —
{"operationName": ..., "query": "...", "variables": {...}}— so the inspector's GraphQL detail view can render the query text and variables as separate panes. - The response body is captured as
{"data": ..., "errors": [...]}(theerrorskey is only present when the response actually returned GraphQL errors). The call is still recorded with HTTP status200andstatusMessage: 'GraphQL errors'in that case — GraphQL reports application-level failures viaerrorsin an otherwise-successful transport response, not via HTTP status codes. - Variables (and anything else in the captured request body text) are
redacted via the same body-pattern redactor every other Jala adapter
uses (
JalaConfig.redactor/JalaRedactor.redactedBodyPatterns). - Transport-level failures (a
LinkExceptionfrom the terminating link, or any other stream error) are captured as aNetworkErrorEvent, same as a connection error injala_dio/jala_http.
Subscriptions #
- The request event fires immediately when the subscription starts
(
operationType: 'subscription'), so the entry appears right away in the inspector, pending. - Every payload delivered on the subscription is captured as a
NetworkSubscriptionPayloadEvent(seqincrementing from 0) and appended to the entry'spayloadstimeline — a ring buffer capped atJalaConfig.maxSubscriptionPayloads(default 50;payloadCountalways reflects the true total, even once older payloads have been evicted). Filter the inspector list withis:subscriptionto see only these entries; the Response tab shows the timeline (tap a payload for the body view). - A single response event still fires when the subscription's stream
closes — not on every payload. Its body uses the first payload's
data/errors, withstatusMessage: 'subscription completed'.durationis the total time the subscription was open (start to close), not the time to the first payload. - v0.4 tagged the total payload count in the completion body as
{"@subscription": {"payloads": N}}; that convention is removed as of v0.5 — superseded by the payload timeline above.
Double-capture #
If the app also wraps its HTTP transport with jala_dio/jala_http —
for example, HttpLink internally uses a Dio/http.Client instance that
already has a JalaDioInterceptor/is already JalaHttp.wrapped — the same
operation is captured twice: once here as a GraphQL entry
(operationName/operationType set, client: 'graphql'), and once more
as a raw HTTP POST by the other adapter. Two ways to avoid the duplicate:
- Don't wrap the transport instance used by the GraphQL client, or
- Filter the inspector list with
-is:graphql(hide GraphQL entries, keep the raw POST) oris:graphql(hide the raw POST, keep the GraphQL entry).
Production safety #
JalaGraphQLLink.requestchecksJalaBinding.instance.isEnabledfirst and returnsforward(request)untouched when Jala is disabled — zero capture work on the hot path, and safe to leave attached in release builds.- A bug in Jala's own capture logic can never break the app's GraphQL
flow: all capture work is wrapped in
try/catch, and the real request is always forwarded — and any real response/error from downstream is always yielded/rethrown — exactly once, regardless of whether capture succeeded. - Large bodies are hard-capped (
JalaConfig.maxBodyBytes, default 512 KB) the same way every other Jala adapter caps them.