Adding a new frontend (client protocol)
This page is for contributors who want clients to connect to QueryFlux using a new wire or HTTP protocol (ingress). It is not about adding a backend engine that runs SQL; for that, see Backend.
What you are building
- A listener that speaks the client’s protocol, parses SQL (and auth), builds
SessionContextandIncomingQuery, and hands work to the shared dispatch layer. - Config so operators can enable the listener (port, TLS, etc.).
- Optional protocol-based routing so traffic from this frontend maps to a default cluster group.
Existing protocols are documented under Frontends. Use them as templates before writing a new one from scratch.
How traffic flows (short)
- Client connects using its protocol.
- Frontend builds
IncomingQuerywith SQL,SessionContext, andFrontendProtocol(your enum variant). - Routers pick a
ClusterGroupName(optionalprotocolBasedentry for your variant). - Translation uses
FrontendProtocol::default_dialect()as the sqlglot source dialect when needed. - Dispatch runs the query on the chosen backend adapter.
- Results flow back through a
ResultSink(or Trino-style async polling for engines that support it).
See the diagram in Frontends overview.
| Dispatch style | Typical use |
|---|---|
execute_to_sink | Most wire/gRPC frontends: run to completion, stream Arrow into a protocol-specific sink. |
dispatch_query | Trino HTTP and other async-capable paths: return handles / nextUri for polling. |
Reference implementations
| Protocol | Crate module | Good template when… |
|---|---|---|
| Trino HTTP | trino_http/ | HTTP, async polling, custom headers |
| PostgreSQL wire | postgres_wire/ | Binary framing, startup/auth, simple sync execution |
| MySQL wire | mysql_wire/ | Similar to Postgres wire, different packet format |
| Flight SQL | flight_sql/ | gRPC / Arrow Flight |
All listeners implement FrontendListenerTrait (async fn listen) in queryflux-frontend/src/lib.rs.
Follow this order
Step 1 — Protocol identity (queryflux-core)
In crates/queryflux-core/src/query.rs:
- Add
FrontendProtocol::YourProtocolto the enum (serdecamelCasein JSON where this appears). - Implement
default_dialect()for your variant: this is the source dialect for sqlglot when translating client SQL. If nothing fits,SqlDialect::Genericis acceptable; you may need to extendSqlDialectand translation rules — see query-translation.md.
Step 2 — Session metadata (queryflux-frontend)
SessionContext is a flat struct — no new variants are needed. In your frontend listener, construct it directly:
SessionContext {
user: /* extracted from your protocol's auth */,
database: /* catalog/schema hint from your protocol */,
tags: /* extracted query tags */,
extra: /* remaining key-value data (headers, params, etc.) */,
}
- Set
useranddatabasefrom your protocol's native fields (e.g. auth header, connection message). - Put everything else — headers, URL params, session variables — into
extraas a flatHashMap<String, String>. Downstream components (routers, Python scripts) can read fromextrausing your protocol's key conventions. - There is no need to touch
crates/queryflux-core/src/session.rs.
Every IncomingQuery carries SessionContext + FrontendProtocol.
Step 3 — YAML config (queryflux-core)
In crates/queryflux-core/src/config.rs:
- Add a field on
FrontendsConfigfor your listener (usuallyOption<FrontendConfig>unless it is always on). Use serdecamelCasefor the YAML key (e.g.myProtocol). FrontendConfigcurrently exposesenabledandportonly. Add fields there only when a knob should apply uniformly to every frontend; otherwise keep protocol-specific options beside the listener implementation.
Step 4 — Listener crate (queryflux-frontend)
- Add
pub mod your_protocol;incrates/queryflux-frontend/src/lib.rs. - Implement a type that implements
FrontendListenerTrait: bind, accept connections, parse protocol, calldispatch_queryorexecute_to_sinkfromdispatch.rswith the correctFrontendProtocolandSessionContext. - Run credentials through the shared auth path like the other frontends.
- Add
Cargo.tomldependencies (codec libraries, gRPC, etc.).
Tip: Copy the smallest existing frontend that resembles yours, then delete protocol-specific code you do not need.
Step 5 — Binary startup (queryflux)
In crates/queryflux/src/main.rs:
- Construct your frontend when config is
Some. - Spawn
listen()alongside Trino / Postgres / … (seeselect!around other frontends). - Thread any protocol-based default group strings into
LiveConfig/ router construction the same waymysql_wireandflight_sqldo.
Hot reload behavior for frontends follows whatever main already does for listeners (many frontends are startup-only; check comments in main.rs).
Step 6 — Protocol-based routing
If operators should map “clients using this protocol” → “default group”:
RouterConfig::ProtocolBasedinconfig.rs— add an optional field (camelCase YAML key).ProtocolBasedRouterincrates/queryflux-routing/src/implementations/protocol_based.rs— add a field and amatcharm onFrontendProtocol.main.rs— when buildingProtocolBasedRouter, pass the configured group name (two places if there is a cold path and a reload path).crates/queryflux-persistence/src/routing_json.rs— extendPROTO_CAMEL_SNAKEso Studio/admin routing JSON validation and group collection understand your new key (see existingtrinoHttp/postgresWireentries).- Document the YAML shape in routing-and-clusters.md if operators rely on it.
Other router types (header, tags, queryRegex, …) usually need no change for a new frontend.
Step 7 — Admin “frontends status”
GET /admin/frontends builds a snapshot from FrontendsConfig in queryflux-frontend/src/admin.rs (build_frontends_status). Add a branch for your frontend so the Admin API and QueryFlux Studio (Protocols page) can show port / enabled state.
Step 8 — Studio (optional)
queryflux-studio/app/protocols/page.tsx— if you want an icon on the Protocols page, extendPROTOCOL_SIMPLE_ICONSkeyed by theidyour admin DTO uses (matchbuild_frontends_status).
Studio does not implement wire protocols; it only displays status from the Admin API.
Step 9 — Tests and docs
- Unit or integration tests in
queryflux-frontendorqueryflux-routing(router tests live incrates/queryflux-routing/tests/router_tests.rs). - Add a page under
website/docs/architecture/frontends/and a row in Frontends overview when the protocol is ready for users.
Checklist
-
FrontendProtocol+default_dialect()inqueryflux-core/src/query.rs - Construct
SessionContextin your listener (no changes tosession.rsneeded) -
FrontendsConfigfield + YAML shape inconfig.rs -
queryflux-frontendmodule +FrontendListenerTrait+ dispatch wiring -
main.rsstartup (and reload paths if applicable) -
ProtocolBasedRouter+RouterConfig::ProtocolBased+main.rswiring (if using protocol-based routing) -
routing_json.rsPROTO_CAMEL_SNAKE(if protocol-based routing is stored/edited via JSON) -
build_frontends_statusinadmin.rs - Studio Protocols icons (optional)
- Tests + user-facing frontend doc
Related reading
- Extending QueryFlux — overview
- Backend
- Frontends overview
- query-translation.md
- routing-and-clusters.md
- observability.md
Key Rust files
crates/queryflux-core/src/query.rs—FrontendProtocol,IncomingQuerycrates/queryflux-core/src/session.rs—SessionContextcrates/queryflux-core/src/config.rs—FrontendsConfig,RouterConfigcrates/queryflux-frontend/src/dispatch.rs—dispatch_query,execute_to_sinkcrates/queryflux-routing/src/implementations/protocol_based.rs— protocol → groupcrates/queryflux/src/main.rs— listener startup, router construction