Skip to content

Consider removing the GraphQL transport until a concrete use case is defined #380

@coretl

Description

@coretl

Consider removing the GraphQL transport until a concrete use case is defined

The GraphQL transport was added as part of the multi-controller foundation. Before it ships as a supported interface, we should agree on who the consumers are — because the two plausible use cases pull in opposite directions and neither is fully served by what we have today.

Use case A — Human explorer

A developer or operator opens a GraphQL playground (e.g. GraphiQL or Apollo Sandbox), introspects the auto-generated schema to discover what the MAIN temperature controller exposes, and reads the current temperature.

With the current typed schema:

# Step 1: discover the schema via introspection (free, built into all GraphQL clients)
query IntrospectMain {
  __type(name: "MAINQuery") {
    fields { name type { name } }
  }
}
# → fields: [temperature, setpoint, rampRate, ...]

# Step 2: fetch the value
query GetTemperature {
  MAIN {
    temperature
  }
}
# → {"data": {"MAIN": {"temperature": 25.3}}}

This use case is well served by the current design. The typed schema is the natural medium for exploratory human use: field names are discoverable, types are checked, and the playground can auto-complete. The extra MAIN wrapper is a minor ergonomic cost.

With a generic getAttribute API:

query GetTemperature {
  getAttribute(path: "MAIN/temperature")
}
# → {"data": {"getAttribute": "25.3"}}   # stringly-typed; no schema guidance

This is actively worse for the human case: the playground cannot auto-complete "MAIN/temperature", the return is untyped, and there is no way to introspect what paths are valid without a separate introspectController call.

Use case B — Machine subscriber (e.g. a TextRead widget)

A display panel has a TextRead widget bound to the MAIN controller's temperature attribute. It needs to receive a pushed update every time the value changes, without polling.

With the current typed schema:

There are no subscriptions. The only option is polling:

# repeated every N seconds — not a subscription
query PollTemperature {
  MAIN { temperature }
}

A strawberry.subscription could be added — it is supported by the library — but it requires a WebSocket transport and an async generator that bridges FastCS's internal attribute-change callbacks to the subscription stream. That plumbing does not exist yet.

With a generic subscribe API:

subscription WatchTemperature {
  subscribe(path: "MAIN/temperature")
}
# → stream of {"data": {"subscribe": "25.3"}} on each change

The generic path-based subscription is actually more practical here than the typed approach: a widget framework driven by string paths (like CSS selectors) can subscribe to any attribute without needing a code-generated client SDK. The loss of static typing matters less when the consumer is machine code that already knows the attribute's type from a separate schema fetch or configuration.

The tension

Typed schema (current) Generic getAttribute
Human explorer ✅ Natural fit; introspection, auto-complete ❌ No schema guidance
Machine bulk-read ✅ One query, many typed fields ✅ Could also work
Machine subscription ❌ Not implemented; hard to express per-field ✅ Simpler to wire to attr callbacks
Code-gen client SDK ✅ Industry standard (Apollo, graphql-codegen) ❌ SDK is useless without types
Odin-control drop-in ❌ Different mental model entirely ✅ Closer to ParameterTree

The core problem is that GraphQL's type system is only a benefit when clients are built against it. We have no such clients. Meanwhile, the one machine use case we can anticipate (subscription updates for display widgets) is not implemented at all — and when it is, the generic path-based form is easier to connect to FastCS's attribute-change infrastructure than a typed field-per-attribute subscription.

Proposal

Remove the GraphQL transport from this PR (or gate it behind an extras install that is explicitly marked experimental) until at least one of the following is true:

  1. A real human-facing client exists that benefits from typed schema introspection.
  2. The subscription mechanism (WebSocket transport + attribute-change → async generator bridge) is implemented and tested, confirming which schema shape is ergonomically correct for that use case.

Shipping the transport now locks in a schema shape ({ MAIN { temperature } }) that may need to change again once subscriptions land, and creates migration burden for consumers we don't yet have.

If the transport is kept, the minimum necessary addition before it can be considered stable is:

  • strawberry.subscription fields that stream attribute value changes, either typed per-field or via a generic subscribe(path: str) — with a concrete display-widget integration validating whichever choice is made.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions