Hyperledger Besu GraphQL API: when reading ledger data in fewer calls matters
The Besu GraphQL endpoint lets an application read blocks, receipts and sync status from a permissioned Besu node in fewer round trips than JSON-RPC. It is worth enabling when a reporting job, reconciliation process or settlement service walks many blocks and needs the receipts for each one, because GraphQL can return the data for a whole block in a single query instead of one JSON-RPC call per receipt. If your workload is mostly sending transactions, checking balances for a few accounts or reading the occasional receipt, JSON-RPC is usually enough and GraphQL adds a second interface to maintain. Before you commit, measure how many calls your job makes per block today and confirm with your integration team how the results will be stored and checked.
GraphQL is served over plain HTTP only. Besu does not offer it over WebSocket, so any consumer that depends on subscriptions or streaming updates will need JSON-RPC or another mechanism. The endpoint listens on by default, and you set its address with graphql-http-host and graphql-http-port. Another important limit is that Besu does not execute pending transactions, so results requested for the Pending` block do not reflect transactions still waiting in the pool. Any reconciliation logic that assumes pending state is current will produce wrong answers, so read only confirmed blocks and document that rule in the job design.
Treat the GraphQL endpoint as a separate API surface. It needs its own decisions on who may reach it, whether it is bound to localhost or a private interface, how requests are authenticated and how access is logged and monitored, in the same way you treat JSON-RPC. Confirm with your security team how the endpoint and its authentication settings fit your existing JSON-RPC controls before exposing it beyond the node host. Test the exposure with a scanner from an approved network segment, and record the result as part of your change evidence.
GraphQL options and when each fits
This table compares the main ways to run the Besu GraphQL endpoint, from loopback-only use to a reverse proxy or internal address, and against relying on JSON-RPC alone. Read each row as a decision: the default bind is local, so widening it is a deliberate step that needs a matching access control. Confirm the exact option names and behavior against the Besu docs for your release before you change a node.
| Option | What it looks like | When it fits | Main risk to check |
|---|---|---|---|
| Default loopback endpoint | Listens on 127.0.0.1 at port 8547 with path /graphql, set by graphql-http-host and graphql-http-port | Single-host reads, admin scripts or a sidecar on the same machine that queries the node | Nothing else on the host can reach it, so other services need a different path |
| JSON-RPC only | GraphQL left disabled, and applications use the existing JSON-RPC interface | Teams already built on JSON-RPC calls and do not need block-level batch reads | Per-receipt calls can add load and latency when indexing whole blocks |
| GraphQL on an internal address | Bound to a private interface on the internal network, reachable from specific hosts | Indexers or reporting services in the same network zone need block and receipt reads | Network rules must limit callers, because the endpoint has no built-in client authentication from this page |
| GraphQL behind a reverse proxy | Node stays on loopback or an internal address, and a proxy terminates HTTPS and enforces access | Several consumers, TLS requirements or audit logging at the edge | Besu does not terminate HTTPS itself, so the proxy configuration becomes part of your security evidence |
| Pending data queries | Queries that ask for Pending state | Rarely needed for settlement reads | Besu does not execute pending transactions, so Pending results do not reflect them |
| API surface decision | Which APIs are enabled and exposed on each node | Set per node role after reviewing what each consumer needs | Confirm the current option set and defaults in the Besu docs before you publish a configuration |
Enabling and checking the GraphQL endpoint
This sequence turns on Besu's GraphQL HTTP endpoint with a deliberate bind address and port, confirms it answers with real ledger data, and verifies it is closed to networks that should not reach it. Each step ends with a check you can repeat after every change.
Decide the bind address and port
Before editing anything, decide which interface the GraphQL endpoint should listen on and which port it will use. Besu's default is 127.0.0.1:8547, which keeps the endpoint local to the node host. Only widen the bind address if a specific application host or internal load balancer must reach it, and record the chosen address, port and the list of networks allowed to connect in your change record. Confirm the port does not collide with JSON-RPC or other services on the host by listing listening sockets with a tool such as ss on Linux. Done means you have a written address, port and allowed-source list that the security team has reviewed.
Set the graphql-http options in node configuration
Add the GraphQL HTTP settings to the node's configuration file, using the graphql-http-host and graphql-http-port keys with the values chosen in the previous step. Keep the change in the same version-controlled configuration your team uses for other node settings, so reviewers can see exactly what was enabled. If you use the default loopback address, the configuration can stay minimal, but state the values explicitly so the intent is visible later. Check the file for syntax errors by running a dry validation or by starting the node in a test environment before production. Done means the diff is reviewed and merged, and the file contains the intended host and port values.
Restart the node and confirm the listener
Restart the Besu node so it reads the updated configuration, then check the startup log for a line showing the GraphQL HTTP service started and the address it bound. On the host, confirm the socket is listening on the expected address and port with ss or netstat. If the log shows an error or the port is missing from the socket list, the most common cause is a typo in the host or port key, so compare the running configuration with the reviewed file. Done means the log and the socket listing both show the endpoint on the agreed address and port.
Send a test query for a known block
From an approved client host, send a GraphQL query over HTTP to the endpoint, asking for a block number or hash you already know exists on the chain, along with its transactions and receipt fields you need. Choose a block that is finalized and well below the head so the result is stable. Compare the returned block hash, parent hash and transaction count against a trusted source such as another node's JSON-RPC response or your block explorer. Done means the GraphQL response matches the independent source for the block and the receipt fields return the values you expect.
Confirm pending data is not treated as executed
Run one query that targets the Pending block tag for an account balance or an estimateGas call, and record the result. Besu does not execute pending transactions, so these results do not reflect transactions waiting in the pool. Document this in the application team's runbook so that no business logic treats a pending-state read as confirmed. Done means the team has seen the behavior on your node and the limitation appears in the integration notes.
Confirm unauthorized networks cannot reach the endpoint
From a host outside the allowed source list, attempt a connection to the GraphQL port on the node's externally reachable address, and from a host inside the allowed list confirm the connection succeeds. Run the same test from the node's own network segment if your firewall rules differ by subnet. A refused or timed out connection from the disallowed host is the expected result. If the connection succeeds from a network that should be blocked, correct the firewall, security group or host rule and repeat the test. Done means the reachability results match the allowed-source list in the change record.
Record the verified configuration and schedule retests
Save the final configuration, the test query and its result, the socket listing and the reachability results together as evidence for the change. Note that GraphQL is not supported over WebSocket, so any client expecting a WebSocket connection to this endpoint must be redesigned. Repeat the reachability and query checks after each Besu upgrade and after any firewall or network change, because a new release or a rule change can alter the exposed surface. Done means the evidence bundle is stored with the node's operational records and the retest trigger is named in your runbook.
Why pending data in GraphQL does not reflect unmined transactions
Besu does not execute pending transactions, so results requested with the Pending block parameter do not include transactions that have been submitted but not yet mined. Account, call and estimateGas results for Pending therefore reflect only the confirmed state, and they can look as if a submitted transaction never happened. The GraphQL endpoint is no different from the node's other read paths in this respect. Treat Pending as a convenience for display and not as a source of truth for anything that moves value.
The consequences reach several parts of a ledger application. A wallet that shows a balance from Pending will omit an outgoing transfer that is waiting in the pool, so users may send a second payment. Nonce handling built on Pending counts can reuse a nonce that an unmined transaction already holds, which leads to replacement or rejected submissions. Gas estimation run against Pending may not account for state changes made by queued transactions, so an estimate can be wrong when those transactions touch the same contracts. Reconciliation jobs that compare Pending results with mined receipts will report false breaks.
For settlement logic, query latest or an explicit block number, and read receipts only for blocks that are final under your consensus design. On a QBFT network, confirm with your own validator and finality assumptions when a block can be treated as settled, and document that rule in the application. Keep pending transaction tracking in a separate component that reads the transaction pool through its own tooling, and test the wallet and nonce flows against a network with transactions in flight before go-live. The Besu GraphQL documentation at Besu GraphQL over HTTP is the reference for which fields accept the Pending parameter.
How to expose GraphQL safely on a regulated network
Besu's GraphQL endpoint is an HTTP service that serves ledger data, so its exposure should be decided before any application depends on it. The cards below cover where the endpoint listens, what sits in front of it, how TLS and authentication are handled, and how access is logged and limited.
Keep the endpoint private
The GraphQL endpoint defaults to which is reachable only from the node host itself. Set the listening address with graphql-http-host and the port with graphql-http-port` only when a specific private interface is required. Before go-live, confirm with a port scan from outside the trust zone that the endpoint is not reachable from user networks, peer networks or the internet.
Front it with an authenticating gateway
Place a reverse proxy or API gateway between application clients and the Besu node, and let it own routing, authentication decisions and rate limits. Only the gateway should reach the GraphQL port. Confirm which identities may call which operations, and whether the gateway can block mutations or unexpected query shapes before they reach the node.
Terminate TLS outside Besu
Besu does not terminate HTTPS natively, so encryption for GraphQL traffic must come from the proxy, gateway or a service mesh in front of the node. Decide where certificates are issued and rotated, which protocol versions and cipher suites the edge accepts, and whether traffic between the gateway and the node stays on a private segment. Document that segment as part of the trust boundary.
Reuse the JSON-RPC authentication model
Besu supports username and password credentials files or JWTs validated against a public key for JSON-RPC. Apply the same identity model to GraphQL at the gateway so that one access review covers both interfaces. Confirm with your own testing whether the GraphQL endpoint can be protected by the node's own authentication settings in your Besu version, since the gateway layer is the control you can verify independently.
Log and monitor every access
Record who called the GraphQL endpoint, when, from which address and with what result, at the gateway, because node logs will not give you a reliable caller identity. Alert on unusual volume, repeated failed authentication and queries that request large ranges of blocks. Feed these logs into the same monitoring stack used for the node so that access evidence is retained under your normal retention rules.
Restrict clients and origins
Allow only the application servers and named services that need ledger reads, and restrict browser access by origin if any front end calls GraphQL directly. Keep the list of permitted clients in version control and review it when applications change. Remember that GraphQL is not supported over WebSocket, so subscription-style streaming to clients must be designed with a different pattern, and WebSocket routes should not be opened for this endpoint.
Questions teams ask before enabling Besu GraphQL
Does GraphQL replace JSON-RPC on a Besu node?
No. GraphQL is an additional read interface on the same node, and JSON-RPC remains the interface most applications, wallets and tooling use for submitting transactions and most standard calls. The main reason to add GraphQL is efficiency for reads. Besu can return data for a whole block in a single GraphQL query, where JSON-RPC would typically need one call per receipt. Teams should keep their existing JSON-RPC paths in place and confirm which calls each application depends on before changing any client code. The Besu GraphQL documentation lists the supported queries.
What is the default GraphQL port, and why should it stay private?
The GraphQL endpoint defaults to configured with the graphql-http-host and graphql-http-port options. The default binds to the loopback address, so it is reachable only from the node host. Keep it that way unless a specific consumer needs network access. GraphQL on Besu does not include its own authentication layer in the way JSON-RPC does, so anything that reaches the port can read the data it serves. Exposure should go through a reverse proxy or gateway that enforces identity, source restrictions and rate limits, and the access policy should be written down before the port is opened.
Does Besu GraphQL work over WebSocket?
No. GraphQL on Besu is supported over HTTP only, and WebSocket is not supported for GraphQL. Applications that need push-style updates should use the WebSocket options that Besu offers for its other APIs, or poll the HTTP endpoint at an interval the application can tolerate. Teams planning a dashboard or event feed should confirm the chosen pattern in a test environment, because the transport choice affects how the proxy and timeouts are configured.
How does GraphQL handle pending transactions?
Besu does not execute pending transactions, so the results of account, call and estimateGas queries for the Pending block do not reflect transactions that are waiting in the pool. Any workflow that needs an accurate balance or gas estimate immediately after submission should read from the latest confirmed block, or wait for a receipt. Applications that show users a pending state should treat it as a label for submitted-but-unconfirmed activity and not as a simulation of the next block. Test this behavior with your own transaction flows before relying on it in a user-facing screen.
How much effort does it take to add GraphQL to an existing node?
For a node that already runs, the configuration change itself is small: set the HTTP host and port options, restrict the bind address, and restart the service. The larger effort sits around it. Teams need to inventory which queries their applications need, design the reverse proxy or gateway route, define who may call the endpoint, add monitoring for request volume and errors, and test the queries against a realistic chain state. Cost depends on how many nodes and consumers are involved, how much access control already exists in the gateway, and whether the work is done in-house or under a fixed-scope engagement. Ask any partner for a written scope that lists each of these items.
What must our team provide before a GraphQL rollout?
Four inputs drive the work. First, a network design statement: which nodes will expose GraphQL, which consumers need it, and how it fits the validator and node roles. Second, the proxy or gateway in front of the endpoint, including its TLS setup and existing identity provider. Third, a written access policy that names the consumers, the permitted queries and the rules for logging and retention. Fourth, a set of test queries that cover blocks, receipts and sync status, run against a non-production node first. Confirm your own change control and whether your security team must approve the new exposure before work begins.
Choosing between GraphQL and JSON-RPC for each read
Keep JSON-RPC as the default for transaction submission and for any method your wallets, indexers, signing services and monitoring scripts already call. Transaction submission, nonce handling and the standard account methods are the parts of your stack that most other tools expect, and moving them to a new interface adds change risk without a clear gain. The GraphQL question is narrower: for reads that fetch a whole block with its transactions and receipts, GraphQL can return that data in a single query instead of one JSON-RPC call per receipt. That is where batching cuts round trips, and where a permissioned network with busy block production or a reporting job pulling many blocks is most likely to benefit.
Test the choice against your own workload rather than assuming the batching gain applies. Measure the query shapes your application actually runs, such as how many blocks per request, how many receipts per block and how often the job polls for sync status, and compare latency and node load under both paths. Record the Besu version in the test, because the GraphQL schema, its fields and its performance characteristics can change between releases, and the calendar release cadence means a result from one version may not hold after an upgrade. Ask the team that owns your node build to confirm which GraphQL fields your version exposes before you commit application code to them.
Do not route pending-state reads through GraphQL on the assumption that it sees unmined transactions. Besu does not execute pending transactions, so account, call and estimateGas results for Pending do not reflect them. If an application depends on pending nonces, balances or gas estimates, keep those calls on JSON-RPC and confirm the behavior in your environment. Document the split in your integration design so that reviewers can see which path serves each read and why.
How DeployOpen scopes and delivers Besu API exposure work
DeployOpen plans, implements, hardens, documents and supports self-hosted open-source platforms inside your own infrastructure, including cloud accounts, data centers and air-gapped networks. For Hyperledger Besu GraphQL API for ledger queries, that means a written scope that fixes which endpoints are exposed, who may reach them and how the setup is tested and handed over.
How we scope
- Inventory which ledger reads your applications need, such as blocks, receipts and sync status, before choosing an endpoint.
- Confirm which node hosts the GraphQL endpoint and whether it sits on a private interface or a shared network.
- Record the access model, callers, network paths and logging expectations in the written scope before work starts.
How we build
- Configure the GraphQL host and port settings on your Besu nodes and document each value used in the deployment.
- Put exposure controls in front of the endpoint, such as a reverse proxy, network policy and transport security you choose.
- Test queries against your expected workloads, including checks that pending data is not relied on for account, call or gas results.
How we hand over
- Deliver configuration files, runbooks and a query catalog that your engineers can review and maintain.
- Walk your operations and security teams through the exposure model, the access rules and the test evidence.
- Document known limits, including GraphQL being unavailable over WebSocket and the need to confirm behavior after Besu upgrades.
How we support
- Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work begins.
- Ongoing support, upgrades and continuous testing are available as a retainer, scoped separately from the build.
- Ask for a written quote tied to your scope, and confirm with your team which upgrade and review responsibilities remain in-house.

