GraphQL
async-graphql Integration
Use toni-async-graphql to add a GraphQL endpoint powered by the async-graphql crate.
toni-async-graphql integrates Toni's module system with the async-graphql crate. The GraphQL context uses async-graphql's Data container — a type-erased map — which makes it easy to add Toni services to resolvers.
Installation
[dependencies]
toni = "0.2"
toni-async-graphql = "0.1"
async-graphql = "7"
toni-axum = "0.1"
tokio = { version = "1", features = ["full"] }1. Define the schema
use async_graphql::{Object, Schema, EmptyMutation, EmptySubscription, Context};
pub struct QueryRoot;
#[Object]
impl QueryRoot {
async fn users(&self, ctx: &Context<'_>) -> Vec<String> {
// Access Toni services via the Data container
let service = ctx.data::<UsersService>().unwrap();
service.find_all_names()
}
async fn user(&self, ctx: &Context<'_>, id: u32) -> Option<String> {
let service = ctx.data::<UsersService>().unwrap();
service.find_one_name(id)
}
}
pub type AppSchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;2. Build the context (inject Toni services)
The ContextBuilder trait tells the integration how to populate the Data container for each request:
use toni_async_graphql::ContextBuilder;
use toni::injectable;
use async_graphql::Data;
#[injectable(pub struct AppContextBuilder {
#[inject]
users_service: UsersService,
})]
impl AppContextBuilder {
pub fn new(users_service: UsersService) -> Self { Self { users_service } }
}
impl ContextBuilder for AppContextBuilder {
fn build_context(&self) -> Data {
let mut data = Data::default();
data.insert(self.users_service.clone());
data
}
}3. Register the GraphQL module
use toni_async_graphql::GraphQLModule;
use async_graphql::{Schema, EmptyMutation, EmptySubscription};
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let graphql_module = GraphQLModule::for_root(schema, AppContextBuilder)
.with_path("/graphql")
.with_playground(true); // disable in production
#[module(
imports: [graphql_module, UsersModule],
)]
pub struct AppModule;4. Wire up in main
use toni::ToniFactory;
use toni_axum::AxumAdapter;
#[tokio::main]
async fn main() {
let mut app = ToniFactory::create(AppModule, AxumAdapter::new()).await;
app.listen(3000, "127.0.0.1").await;
}Visit http://localhost:3000/graphql in a browser to open the GraphQL Playground.
Using guards on the GraphQL endpoint
Apply guards to the GraphQLController via the module configuration:
let graphql_module = GraphQLModule::for_root(schema, AppContextBuilder)
.with_path("/graphql")
.with_guard(AuthGuard {})
.with_playground(false);Subscriptions
For GraphQL subscriptions over WebSocket, configure the subscription transport:
use toni_async_graphql::GraphQLModule;
use async_graphql::{Schema, EmptyMutation};
let schema = Schema::new(QueryRoot, MutationRoot, SubscriptionRoot);
let graphql_module = GraphQLModule::for_root(schema, AppContextBuilder)
.with_subscriptions("/graphql/ws");