juniper_axum
Axum handlers and extractors for serving Juniper GraphQL queries, mutations, and WebSocket subscriptions.
Repository Health
Technical Analysis
juniper_axum wires the Juniper GraphQL implementation into the Axum web framework. It ships a ready-to-mount graphql handler, a JuniperRequest/JuniperResponse extractor pair that implements Axum’s FromRequest/IntoResponse traits, and graphiql/playground handlers for interactive API exploration, so an existing Axum router can serve a full GraphQL API with a single .route() call and an Extension<Schema> layer.
Behind a subscriptions feature flag, the crate also adds WebSocket support that auto-negotiates between the legacy graphql-ws and current graphql-transport-ws GraphQL-over-WebSocket protocols, so real-time subscriptions run on the same Axum server without pulling in a separate GraphQL runtime. It is maintained as part of the graphql-rust/juniper workspace and versioned independently from the core juniper crate.
What You Get
- A
graphqlhandler function that executes GraphQL requests against anyjuniper_graphql_ws::Schemavia anExtensionlayer JuniperRequest/JuniperResponsetypes implementing Axum’sFromRequest/IntoResponsetraits for building custom handlersgraphiql()andplayground()handlers that serve interactive GraphQL IDEs pointed at your query and subscription routes- A feature-gated
subscriptionsmodule implementing both the legacygraphql-wsand currentgraphql-transport-wsWebSocket protocols - Integration test suites (
http_test_suite,ws_test_suite) and two runnable examples demonstrating request/response and subscription flows
Common Use Cases
- Mounting a GraphQL endpoint on an existing Axum-based HTTP API
- Serving GraphQL subscriptions over WebSocket in real-time applications
- Providing GraphiQL/Playground UIs for local development and internal tooling
- Building custom authentication-aware GraphQL handlers on top of Axum’s extractor traits
Under The Hood
Architecture
juniper_axum is organized as thin adapter modules over Axum’s extractor/handler traits: extract.rs defines JuniperRequest implementing FromRequest, dispatching on HTTP method and Content-Type to build a GraphQLBatchRequest (GET query string via a hand-rolled GetRequest struct working around a Deserialize limitation, POST JSON via axum::Json, POST application/graphql as a raw string); response.rs defines JuniperResponse implementing IntoResponse, mapping the batch response to a 200 or 400 status depending on GraphQLBatchResponse::is_ok(); lib.rs composes these into ready-to-use handler functions graphql(), graphiql(), and playground() that need only an Extension<Schema> layer; and subscriptions.rs (feature-gated) adds ws(), graphql_ws(), and graphql_transport_ws() handler factories that upgrade Axum’s WebSocketUpgrade extractor and multiplex both GraphQL-over-WebSocket protocols via juniper_graphql_ws. There is no internal state beyond these adapters — schema and context ownership stays entirely with juniper and juniper_graphql_ws — so the crate’s surface area tracks Axum’s extractor/handler trait shapes closely.
Tech Stack
Built for Rust 2024 edition with an MSRV of 1.85. Core dependencies are axum (JSON and query features, no default features), juniper and juniper_graphql_ws as path dependencies within the same Cargo workspace, and serde/serde_json for (de)serialization; the optional subscriptions feature pulls in derive_more and futures. Dev-dependencies bring in a multi-threaded tokio runtime, tokio-tungstenite for WebSocket test clients, tower-service, and tracing. It ships two runnable examples built against tokio::main and is released to crates.io independently from its own changelog inside the graphql-rust/juniper monorepo.
Code Quality
Both extract.rs and subscriptions.rs carry inline unit tests covering GET, JSON POST, and application/graphql POST content-type branches (including charset handling), plus two integration-test suites under tests/ that exercise the crate through real Axum routers. Errors are returned as explicit typed Response values built from (StatusCode, message) tuples rather than panicking or being swallowed, and the crate’s Cargo.toml lint table turns on missing_docs, unsafe_code = "forbid", future_incompatible, and several pedantic Clippy lints as CI-enforced warnings. Every public item carries doc comments with runnable doctest examples.
API Design
The public API is two-layered: graphql(), graphiql(), playground(), and ws() are drop-in Axum Handler-compatible functions needing only an Extension<Arc<Schema>> layer, so a minimal integration is a single .route() call — but extract::JuniperRequest and response::JuniperResponse are also exported standalone for callers who need to pull a request-scoped Schema::Context (for example, an authenticated user) instead of the default Default-context shortcut. WebSocket setup accepts either a pre-built ConnectionConfig or an async closure for connection-time authentication, covering static and dynamic auth without extra abstraction; the main ergonomic friction is the generic Schema/ScalarValue bounds surfacing in handler type signatures, which is inherent to Juniper’s generic design rather than something juniper_axum introduces.