Layer: L7 Application ยท Build flags: PROTOCORE_ENABLE_GRAPHQL
What this example teaches
GraphQL lets the client say exactly which fields it wants in one request. POST a query to /graphql and the device resolves the selected fields and returns a {"data":{...}} response shaped by the query - so the same endpoint serves a minimal phone client and a rich dashboard without new routes.
A resolver answers one scalar per dotted path. Nested selections become dotted paths (net.rssi); arguments are visible to the resolver for that field:
static bool resolver(const char *path, const protocore_gql_args *args, protocore_gql_value *out) {
if (!strcmp(path, "heap")) { out->type = protocore_gql_type::PROTOCORE_GQL_INT; out->i = ESP.getFreeHeap(); return true; }
if (!strcmp(path, "net.rssi")) { out->type = protocore_gql_type::PROTOCORE_GQL_INT; out->i = Physical.wifi->rssi(); return true; }
if (!strcmp(path, "greet")) {
const char *who = "?";
protocore_gql_arg_str(args, "name", &who);
static char b[64]; snprintf(b, sizeof(b), "hi %s", who);
out->type = protocore_gql_type::PROTOCORE_GQL_STR; out->s = b; return true;
}
return false;
}
A field with a sub-selection (like net) is an object the engine builds by recursing into the resolver for each child; returning false yields null.
One call executes a query. The engine parses the body, walks the selection set, and writes the response envelope:
protocore_gql_result rc = protocore_graphql_execute(req->body, req->body_len, resolver, body, sizeof(body));
server.send(id, rc == protocore_gql_result::PROTOCORE_GQL_OK ? 200 : 400, "application/json", body);
It writes {"data":...} on success or {"errors":...} on a parse error; answering 200 with the GraphQL error envelope is the conventional reply.
Build and run
pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPROTOCORE_ENABLE_GRAPHQL=1" \
--lib="." examples/L7-Application/GraphQL/GraphQL.ino
curl -s --data '{ heap uptime net { rssi } }' http://<ip>/graphql
# {"data":{"heap":210000,"uptime":42,"net":{"rssi":-50}}}
curl -s --data '{ greet(name: "world") }' http://<ip>/graphql
# {"data":{"greet":"hi world"}}
Annotated source
The complete sketch (GraphQL.ino), reproduced verbatim with added explanatory comments:
#define PROTOCORE_ENABLE_GRAPHQL 1
#include "protocore.h"
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
PC server;
static bool resolver(const char *path, const protocore_gql_args *args, protocore_gql_value *out)
{
if (!strcmp(path, "heap"))
{
out->type = protocore_gql_type::PROTOCORE_GQL_INT;
out->i = ESP.getFreeHeap();
return true;
}
if (!strcmp(path, "uptime"))
{
out->type = protocore_gql_type::PROTOCORE_GQL_INT;
out->i = millis() / 1000;
return true;
}
if (!strcmp(path, "net.rssi"))
{
out->type = protocore_gql_type::PROTOCORE_GQL_INT;
out->i = Physical.wifi->rssi();
return true;
}
if (!strcmp(path, "net.ip"))
{
static char ip[20];
uint32_t v4 = Physical.link->egress_ip();
snprintf(ip, sizeof(ip), "%u.%u.%u.%u", (unsigned)(v4 & 0xFF), (unsigned)((v4 >> 8) & 0xFF),
(unsigned)((v4 >> 16) & 0xFF), (unsigned)((v4 >> 24) & 0xFF));
out->type = protocore_gql_type::PROTOCORE_GQL_STR;
out->s = ip;
return true;
}
if (!strcmp(path, "greet"))
{
const char *who = "?";
protocore_gql_arg_str(args, "name", &who);
static char b[64];
snprintf(b, sizeof(b), "hi %s", who);
out->type = protocore_gql_type::PROTOCORE_GQL_STR;
out->s = b;
return true;
}
return false;
}
void setup()
{
Serial.begin(115200);
Physical.wifi->init(SSID, PASSWORD);
while (!Physical.wifi->ready())
delay(250);
Serial.print("IP: ");
uint32_t ip = Physical.link->egress_ip();
Serial.printf("IP: %u.%u.%u.%u\n", (unsigned)(ip & 0xFF), (unsigned)((ip >> 8) & 0xFF),
(unsigned)((ip >> 16) & 0xFF), (unsigned)((ip >> 24) & 0xFF));
char body[512];
protocore_gql_result rc = protocore_graphql_execute((
const char *)req->
body, req->
body_len, resolver, body,
sizeof(body));
server.send(id, rc == protocore_gql_result::PROTOCORE_GQL_OK ? 200 : 400, "application/json", body);
});
server.begin(80);
}
void loop()
{
server.handle();
}
Zero-heap GraphQL executor over a query subset (PROTOCORE_ENABLE_GRAPHQL).
@ HTTP_POST
Non-idempotent create / action.
Layer 1 (Physical) - link bring-up, the interface registry, and live egress reporting.
uint8_t body[BODY_BUF_SIZE+1]