Introduction
In Week 1, we rebooted the Rubrik PHP framework and set a clear direction: build a modern, reusable toolkit around the Rubrik Security Cloud APIs.
In Week 2, we explored the shift from REST to GraphQL and got comfortable navigating the RSC API Playground.
In Week 3, we implemented authentication using the OAuth2 Client Credentials flow and introduced a reusable helper function, rkRscGetToken(), to retrieve a Bearer token.
Now it's time to connect all the pieces.
This week, we execute our first real GraphQL query against Rubrik Security Cloud, directly from PHP.
From the Playground to Real Code
Now that authentication is in place and we understand how GraphQL queries are structured, we can finally interact with real data in Rubrik Security Cloud.
Before writing any PHP, the right approach is always the same: validate your query in the RSC API Playground.
In Week 2, we used the Playground to explore the schema. Now we'll reuse it to build a minimal working query.
Our goal is simple: list SLA Domains.
query listSlaDomains {
slaDomains {
nodes {
id
name
}
}
}This query does three things: calls the slaDomains GraphQL endpoint, retrieves the list of SLAs via nodes, and limits the response to id and name.
Practitioner Tip "Start simple. In GraphQL, it's easy to overfetch early. The discipline is to request only what you need until your execution pipeline is fully stable. Every field you add is a field you have to parse, validate, and maintain. Build the query in layers: get it working with minimal fields first, then expand as requirements become concrete." |
A typical response will look like this:
{
"data": {
"slaDomains": {
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "Gold"
},
{
"id": "00000000-0000-0000-0000-000000000001",
"name": "Silver"
}
]
}
}
}This structure directly mirrors your query. Data is the root of the successful response, slaDomains is the object you queried, nodes is the list of SLA Domains, and each object represents one SLA with its properties.
This is a core GraphQL concept: the shape of the response is defined by the shape of your query.
Practitioner Tip "This is one of GraphQL's biggest advantages over REST. You control both payload size and structure. That becomes critical later when dealing with large datasets or performance-sensitive workflows. In REST, you receive whatever the endpoint decides to return. In GraphQL, you are the one making that decision, and that matters when you are building automation at scale." |
Understanding nodes
The nodes field is important and appears frequently in RSC GraphQL queries. It represents a collection of objects returned by the query.
In this case, each node equals one SLA Domain, and the list equals all SLA Domains available in your environment.
Later, you will encounter more complex structures involving pagination, edges, and cursors. For now, think of nodes as a simple array of results.
Playground as Your Validation Tool
The Playground is not just for testing queries. It is your primary tool to explore available fields, validate query syntax, understand response structures, and iterate quickly without touching code.
Every function you build later in the framework should start here. If it doesn't work in the Playground, it won't work in PHP.
Transition to Code
Once your query is validated in the Playground, you have confirmed three things before writing a single line of PHP: the query syntax is correct, the response structure matches what you expect, and real data from your RSC environment is flowing back. That validation step is not optional. It collapses the debugging surface in half. When something breaks in PHP, you already know the query itself is sound — which means the problem is in your HTTP layer, authentication, or response handling, not the GraphQL logic.
Now we are ready to execute this same query from PHP using the token we built in Week 3.
Understanding the GraphQL Call Structure
Now that the query works in the Playground, the next step is to execute it programmatically.
Rubrik Security Cloud exposes GraphQL through a single endpoint:
- Endpoint: https://<your-tenant>/api/graphql
- Method: POST
- Headers:
- Content-Type: application/json
- Authorization: Bearer <ACCESS_TOKEN>
- Body:
{
"query": "your GraphQL query here"
}In our framework, RSC_GRAPHQL_URL is derived from the RSC_FQDN environment variable introduced in Week 3.
This is a key shift from REST: you always call the same endpoint, the query defines what you retrieve, and the response mirrors your query. At this stage, we simply reuse the token obtained in Week 3 via rkRscGetToken().
Raw cURL Validation
Before integrating into PHP, it's useful to validate the full request using cURL.
RSC_TOKEN="your_token_here"
RSC_GRAPHQL_URL="https://yourorg.my.rubrik.com/api/graphql"
QUERY='query listSlaDomains {
slaDomains {
nodes {
id
name
}
}
}'
cURL -X POST "$RSC_GRAPHQL_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $RSC_TOKEN" \
-d "{\"query\":\"$QUERY\"}"
If everything is correct, you should get the same structure as in the Playground.
Practitioner Tip "Always keep a working cURL example. It is your lowest-level validation tool when something breaks in your application stack. When a PHP call fails, a working cURL example tells you immediately whether the issue is in your code or in the environment. Without it, you are debugging blind; chasing PHP logic when the real problem might be a token expiry, a firewall rule, or a misconfigured endpoint." |
At this point, you have validated that authentication works, the endpoint is correct, and the query is valid. Now we can integrate this into PHP.
Executing the Query in PHP
We now reuse everything we built so far.
First, retrieve the access token:
$accessToken = rkRscGetToken();Then define the query:
$query = <<<'GQL'
query listSlaDomains {
slaDomains {
nodes {
id
name
}
}
}
GQL;Prepare the JSON payload:
$payload = json_encode(['query' => $query], JSON_THROW_ON_ERROR);Now send the request using cURL:
$ch = cURL_init(RSC_GRAPHQL_URL);
cURL_setopt_array($ch, [
cURLOPT_POST => true,
cURLOPT_RETURNTRANSFER => true,
cURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $accessToken,
],
cURLOPT_POSTFIELDS => $payload,
]);
$responseBody = cURL_exec($ch);
if ($responseBody === false)
{
throw new RuntimeException('cURL error: ' . cURL_error($ch));
}
$statusCode = cURL_getinfo($ch, cURLINFO_HTTP_CODE);
At this stage, $responseBody contains the raw JSON response returned by Rubrik Security Cloud.
Parsing and Validating the Response
GraphQL introduces an important behavior: errors can be returned even when the HTTP status is 200. Both levels must be validated.
Practitioner Tip "Many developers incorrectly assume HTTP 200 means success. In GraphQL, transport success and query success are two different things. A request that authenticates and reaches the endpoint can still return a perfectly formatted error payload with a 200 status. Build your validation layer to check both the HTTP code and the GraphQL errors key every time. Missing this check is one of the most common sources of silent failures in production automation." |
$data = json_decode($responseBody, true);
if ($statusCode !== 200)
{
$msg = $data['errors'][0]['message'] ?? ('HTTP status ' . $statusCode);
throw new RuntimeException('GraphQL HTTP error: ' . $msg);
}
if (!empty($data['errors']))
{
$msg = $data['errors'][0]['message'] ?? 'Unknown GraphQL error';
throw new RuntimeException('GraphQL error: ' . $msg);
}
$nodes = $data['data']['slaDomains']['nodes'] ?? [];At this point, $nodes contains the SLA Domains exactly as returned by the API.
Normalizing the Output
The raw GraphQL structure is not ideal for reuse within the framework. We convert it into a clean and predictable structure:
$result = [];
foreach ($nodes as $node)
{
$result[] = [
'id' => $node['id'] ?? null,
'name' => $node['name'] ?? null,
];
}This removes GraphQL-specific nesting and exposes a clean structure to the rest of the framework.
Implementing rkGetSLAs()
We now wrap everything into a reusable function.
function rkGetSLAs(?string $accessToken = null): array
{
if ($accessToken === null)
{
$accessToken = rkRscGetToken();
}
$query = <<<'GQL'
query listSlaDomains {
slaDomains {
nodes {
id
name
}
}
}
GQL;
$payload = json_encode(['query' => $query], JSON_THROW_ON_ERROR);
$ch = cURL_init(RSC_GRAPHQL_URL);
cURL_setopt_array($ch, [
cURLOPT_POST => true,
cURLOPT_RETURNTRANSFER => true,
cURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $accessToken,
],
cURLOPT_POSTFIELDS => $payload,
]);
$responseBody = cURL_exec($ch);
if ($responseBody === false)
{
$err = cURL_error($ch);
throw new RuntimeException('cURL error in rkGetSLAs(): ' . $err);
}
$statusCode = cURL_getinfo($ch, cURLINFO_HTTP_CODE);
$decoded = json_decode($responseBody, true);
if ($statusCode !== 200)
{
$msg = $decoded['errors'][0]['message'] ?? ('HTTP status ' . $statusCode);
throw new RuntimeException('GraphQL HTTP error in rkGetSLAs(): ' . $msg);
}
if (!empty($decoded['errors']))
{
$msg = $decoded['errors'][0]['message'] ?? 'Unknown GraphQL error';
throw new RuntimeException('GraphQL error in rkGetSLAs(): ' . $msg);
}
$nodes = $decoded['data']['slaDomains']['nodes'] ?? [];
$result = [];
foreach ($nodes as $node)
{
$result[] = [
'id' => $node['id'] ?? null,
'name' => $node['name'] ?? null,
];
}
return $result;
}Standard Execution Pattern
At this point, a clear pattern emerges. Every function in this framework will follow the same structure: define the GraphQL query, send the request, decode the response, validate errors, and normalize the output.
rkGetSLAs() is the first implementation of this pattern.
This is not just code organization. It is a contract that every data retrieval function in the framework honors. Once this pattern is internalized, adding new functions becomes mechanical — the logic changes, the structure does not. That consistency is what allows automation scripts to trust the data they receive without defensive checks at every call site.
Quick Test
$slas = rkGetSLAs();
foreach ($slas as $sla)
{
echo $sla['name'] . " (" . $sla['id'] . ")\n";
}If this works, your GraphQL pipeline is fully operational.
What You Just Built
This week closed the loop between authentication and real data retrieval. You now have a working authentication layer that handles token acquisition automatically, a complete GraphQL execution flow from query definition through response normalization, and a reusable function that abstracts all of that complexity behind a clean, predictable interface. The framework is no longer theoretical. It is operational.
Practitioner Tip "This is the real milestone: not the query itself, but the fact that you now have a repeatable, production-ready pattern. The specific query you wrote today matters less than the execution pipeline you built around it. Every future function in this framework will be built the same way. The investment you made in structure here pays dividends across every subsequent week of this series." |
What's Next: Scaling rkGetSLAs() for Production
In Week 4, we focused on the fundamentals: executing a GraphQL query, handling authentication, parsing and validating responses, and returning clean, reusable data. This is the foundation of the framework.
The example we used is intentionally simple. In real environments, you will deal with large datasets, need to filter results, and need to control how much data is returned. In Week 5, we will extend this exact function and introduce filtering SLA Domains by name, pagination using edges, cursors, and limits, and safe handling of larger result sets. We will evolve rkGetSLAs() into something closer to a production-ready data access layer.
Practitioner Tip "The biggest trap at this stage is assuming that "it works" means "it scales". GraphQL queries that return small datasets behave very differently when dealing with thousands of objects. Design for scale from the start, even when your current dataset is small. The pagination pattern you add next week is not a future concern; it is a baseline requirement for any framework you intend to run in production." |
Week 5 is where we move from working code to robust code.
Contributed by

Frederic Lhoest
Senior Technology Architect, PCCW Global

Mike Preston
Staff Technical Marketing Manager, Rubrik








