How does a client query Selenium Grid's /graphql endpoint to read live session and queue state?
answer
- The console is not the only reader
- One endpoint, one root query type
- Not a GET, it takes a body
- grid, nodesInfo, sessionsInfo, session by id
- POST /graphql with a query field
basics
~10 sSelenium 4's Grid exposes a read-only GraphQL API at /graphql. You POST a JSON body holding a query string, and the schema offers four roots: grid, nodesInfo, sessionsInfo and session by id.
solid answer
~40 sSelenium 4's Grid serves a read-only GraphQL schema at `/graphql` on the router, hub or standalone port. The route accepts `POST` (and `OPTIONS` for CORS preflight) — a `GET` does not match — with a JSON body like `{"query": "{ grid { sessionQueueSize } }"}` plus an optional `variables` object; a body with no string `query` comes back as HTTP 500 and `Unable to find query`. The single `GridQuery` root offers `grid` (`totalSlots`, `nodeCount`, `maxSession`, `sessionCount`, `sessionQueueSize`, `version`, `uri`), `nodesInfo` (`nodes` with `status`, `stereotypes`, `slotCount`, `sessionCount`, `osInfo`), `sessionsInfo` (`sessions` and `sessionQueueRequests`), and `session(id:)`. There are no mutations. The `/ui` console is simply a browser client of this endpoint, re-polling it every five seconds.
code
bash · 7 linescurl -s -X POST -H "Content-Type: application/json" \
--data '{"query":"{ grid { nodeCount totalSlots sessionCount sessionQueueSize } }"}' \
http://localhost:4444/graphql
curl -s -X POST -H "Content-Type: application/json" \
--data '{"query":"{ sessionsInfo { sessionQueueRequests sessions { id nodeUri sessionDurationMillis } } }"}' \
http://localhost:4444/graphqlgo deeper
Know that the grid has a GraphQL endpoint at /graphql and that the console reads from it. Being able to paste one curl and get a session count back is the level expected here.
Explain the call mechanics: POST with a JSON body carrying a query string, the four roots of GridQuery, and why capability payloads arrive as strings you must parse yourself.
Show how you turn it into operations: scripted queue-depth and session-age checks, reading it when the console is disabled, and knowing it observes but never mutates the grid.
Own what the team standardises on. Decide whether grid state is scraped ad hoc or exported into the monitoring the organisation already runs, and who owns that contract when the schema shifts.
## Where the endpoint lives and how it is called **Selenium 4's Grid** ships a read-only **GraphQL** API at `/graphql`, served by the same process that serves sessions: the `router`, the `hub`, or a `standalone`. It is registered for two HTTP methods only — `POST` for real queries and `OPTIONS` for the CORS preflight a browser sends. A `GET /graphql` matches no route, so opening the URL in a browser tab is not how you use it. A call is an ordinary JSON `POST`: - `query` — **required**, a string holding the GraphQL document. If the body has no string under this key, the handler answers **HTTP 500** with the plain text `Unable to find query`. - `variables` — optional, an object; anything that is not an object is treated as empty. ```bash curl -s -X POST -H "Content-Type: application/json" \ --data '{"query":"{ grid { nodeCount totalSlots sessionCount sessionQueueSize } }"}' \ http://localhost:4444/graphql ``` ## The schema in one screen There is a single root type, `GridQuery`, with four fields: | Root field | Returns | Use it for | |---|---|---| | `grid` | `uri`, `totalSlots`, `nodeCount`, `maxSession`, `sessionCount`, `version`, `sessionQueueSize` | the one-line summary of the whole grid | | `nodesInfo` | `nodes` — each with `id`, `uri`, `status`, `maxSession`, `sessionTimeout`, `slotCount`, `sessionCount`, `stereotypes`, `version`, `osInfo`, `sessions` | what capacity exists and what it advertises | | `sessionsInfo` | `sessions` plus `sessionQueueRequests` | what is running and what is waiting | | `session(id:)` | one `Session` | drilling into a single session by id | A `Session` carries `id`, `capabilities`, `startTime`, `uri`, `nodeId`, `nodeUri`, `sessionDurationMillis` and its `slot`; a `Slot` carries `id`, `stereotype` and `lastStarted`. Node `status` is the enum `UP`, `DRAINING` or `DOWN`. Two field-level details matter in practice: - `capabilities`, `stereotype` and `stereotypes` are declared as **`String!`**, not as objects. The server serialises the capability maps to JSON text and hands them back as scalars, so the caller parses the string itself. - `sessionQueueRequests` is a list of **strings**, one per waiting request, each the JSON of that request's capabilities. `grid { sessionQueueSize }` gives you the same queue as a bare count. ## The console is a client, not a separate API The Grid UI at `/ui` is a browser application that talks to this very endpoint. Its **Overview** screen runs a `nodesInfo` query; its **Sessions** screen runs a `sessionsInfo` query and renders `sessionQueueRequests` as a queued-sessions panel; a summary query feeds the header counters. It re-runs them on a **five-second poll**, which is why the console tables refresh on their own. Two consequences follow, and both are worth knowing: 1. **Anything the console shows, `curl` can fetch** — the console has no privileged data source. 2. **Disabling the console does not disable the data.** Starting the grid with `--disable-ui` drops the `/ui` routes, but `/graphql` is registered separately and keeps answering. So does `/status`. Requesting the root path `/` redirects to `/ui/`, and the Selenium 3-era `/grid/console` path redirects there too. When a browser page is served from a different origin than the grid, the grid needs `--allow-cors true` for the preflight to succeed. ## What the endpoint deliberately does not do The schema declares `schema { query: GridQuery }` and nothing else — there are **no mutations and no subscriptions**. You cannot kill a session, drain a node, or re-queue a request through `/graphql`; it is an observation surface only. There is also no filtering or paging on `sessions` or `nodes`: you fetch the lists and filter client-side. ## Reading it against a permit-checklist grid A team runs a **construction-permit checklist** suite across a shared grid and wants a five-second answer to "is my run starved or just slow?". Two queries do it. `{ grid { sessionQueueSize sessionCount maxSession } }` says how much of the grid is in use and how much work is waiting. `{ sessionsInfo { sessions { id capabilities sessionDurationMillis nodeUri } } }` says which sessions are holding the slots and how long each has been alive — a permit-checklist session sitting at forty minutes of `sessionDurationMillis` is usually a leaked driver, not a slow test. ## Failure modes to recognise - A `GET` returns whatever the catch-all route gives you, not a GraphQL error — send a `POST`. - A body missing `query` returns HTTP 500 and the text `Unable to find query`; it is a malformed request, not a broken grid. - Asking for a composite field without selecting subfields is a schema error: GraphQL requires you to descend until every leaf is a scalar.
- The grid runs with --disable-ui; can you still read queue depth?Yes. `--disable-ui` only removes the `/ui` console routes from the router, hub or standalone handler. `/graphql` is registered separately and keeps answering, so `{ grid { sessionQueueSize } }` and `{ sessionsInfo { sessionQueueRequests } }` still work, and `/status` is unaffected as well.
- Why do capabilities and stereotypes come back as strings rather than objects?The Selenium 4 Grid schema declares `Session.capabilities`, `Slot.stereotype` and `Node.stereotypes` as `String!`. The server serialises each capability map to JSON text and returns it as a scalar, so the caller parses the string itself. That keeps the schema stable while capability keys stay open-ended.
- Can you terminate a stuck session through the GraphQL endpoint?No. The schema declares only `schema { query: GridQuery }` — there are no mutations and no subscriptions, so `/graphql` is an observation surface. Ending a session means the WebDriver delete-session call on the grid, not a GraphQL call.
saying these in an interview costs you the question
- Tries to fetch /graphql with a GET in a browser tab
- Believes disabling the Grid UI also removes the GraphQL endpoint
- Expects mutations that can kill a session or drain a node
- Thinks stereotypes returns structured objects rather than a string
- Assumes the console can show data the endpoint cannot return