DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
graphql.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file graphql.h
6 * @brief Zero-heap GraphQL query subset - parser + executor (DETWS_ENABLE_GRAPHQL).
7 *
8 * A small, deterministic GraphQL *query* engine for a constrained device: it
9 * parses a query document into a fixed AST node pool (no heap), then walks the
10 * selection set emitting a `{"data":{...}}` response that mirrors exactly the
11 * fields requested - the core GraphQL property (the client picks the shape).
12 *
13 * **Schema-free model.** There is no separate schema: a field that carries a
14 * sub-selection (`obj { a b }`) is an object - the engine recurses and emits the
15 * nested object - and a field with no sub-selection is a leaf scalar, for which
16 * the engine calls your single resolver. Arguments encountered along the path
17 * (`sensor(id: 2) { value }`) are collected and handed to the leaf resolver, so a
18 * resolver for `sensor.value` can read `id`. The app implements one function: "the
19 * value of the scalar at this dotted path, given these args."
20 *
21 * Supported: a single query operation (bare `{...}` or `query [Name] {...}`),
22 * nested selection sets, field arguments (int / float / string / bool / null),
23 * and insignificant commas. Out of scope (keeps it bounded + deterministic):
24 * mutations, subscriptions, fragments, variables, directives, aliases, lists of
25 * objects. Malformed input fails closed with `{"errors":[...]}`.
26 *
27 * Pure and host-tested. Bounds are compile-time (DETWS_GQL_*); parsing and
28 * execution allocate nothing.
29 *
30 * @author Douglas Quigg (dstroy0)
31 * @date 2026
32 */
33
34#ifndef DETERMINISTICESPASYNCWEBSERVER_GRAPHQL_H
35#define DETERMINISTICESPASYNCWEBSERVER_GRAPHQL_H
36
37#include "ServerConfig.h"
38#include <stddef.h>
39#include <stdint.h>
40
41#if DETWS_ENABLE_GRAPHQL
42
43/** @brief Scalar value kinds a resolver can return. */
44enum class DetwsGqlType : uint8_t
45{
46 DETWS_GQL_NULL = 0,
47 DETWS_GQL_INT,
48 DETWS_GQL_FLOAT,
49 DETWS_GQL_BOOL,
50 DETWS_GQL_STR, ///< s points to a NUL-terminated string stable for the call.
51};
52
53/** @brief A scalar value (resolver output, or an argument). */
54struct DetwsGqlValue
55{
56 DetwsGqlType type; ///< the value's type.
57 long long i;
58 double f;
59 bool b;
60 const char *s;
61};
62
63/** @brief Opaque view of the arguments in scope at a resolved field. */
64struct DetwsGqlArgs;
65
66/** @brief Read an int argument @p name; false if absent / not an int. */
67bool detws_gql_arg_int(const DetwsGqlArgs *args, const char *name, long long *out);
68/** @brief Read a string argument @p name; false if absent / not a string. */
69bool detws_gql_arg_str(const DetwsGqlArgs *args, const char *name, const char **out);
70/** @brief Read a bool argument @p name; false if absent / not a bool. */
71bool detws_gql_arg_bool(const DetwsGqlArgs *args, const char *name, bool *out);
72
73/**
74 * @brief Resolve the scalar leaf at dotted @p path (e.g. "device.uptime").
75 *
76 * Fill @p out with the value and return true; return false to emit JSON null.
77 * @p args exposes every argument in scope along the path.
78 */
79typedef bool (*detws_gql_resolver_fn)(const char *path, const DetwsGqlArgs *args, DetwsGqlValue *out);
80
81/** @brief detws_graphql_execute() result codes. */
82enum class DetwsGqlResult : int32_t
83{
84 DETWS_GQL_OK = 0, ///< Executed; @p out holds `{"data":{...}}`.
85 DETWS_GQL_ERR_PARSE = -1, ///< Malformed query (syntax / unsupported construct).
86 DETWS_GQL_ERR_LIMIT = -2, ///< Exceeded a DETWS_GQL_* bound (nodes/args/depth/name).
87 DETWS_GQL_ERR_OVERFLOW = -3 ///< Response did not fit @p cap.
88};
89
90/**
91 * @brief Parse and execute a GraphQL query, writing the JSON response.
92 *
93 * On success writes `{"data":{...}}`; on a parse/limit error writes
94 * `{"errors":[{"message":"..."}]}` (and still returns the negative code) when it
95 * fits, else nothing.
96 *
97 * @param query,len the query document.
98 * @param resolver leaf resolver (may be nullptr -> every leaf is null).
99 * @param out,cap response buffer and capacity.
100 * @return ::DETWS_GQL_OK or a negative ::DetwsGqlResult.
101 */
102DetwsGqlResult detws_graphql_execute(const char *query, size_t len, detws_gql_resolver_fn resolver, char *out,
103 size_t cap);
104
105#endif // DETWS_ENABLE_GRAPHQL
106#endif // DETERMINISTICESPASYNCWEBSERVER_GRAPHQL_H
User-facing configuration for DeterministicESPAsyncWebServer.