04
Product
16
Backend
09
Auth
12
iOS
07
Infra
02
Real-Time

Inject services via async-graphql Data context

ADR-0052 ACCEPTED · 2025-07-12
Service injection via async-graphql Data context

Context

Resolvers need access to services (database pools, external API clients, domain services). Options: a DI framework, a global singleton, or async-graphql's built-in Data container.

Decision

Construct all services in build_graphql_data() at startup and insert them into the GraphQL schema's Data container. Resolvers access services via extension traits on async_graphql::Context:

// startup/router.rs
let authz_service = AuthzService::new(state.db_pool.clone());
data.insert(authz_service);

// In any resolver
let authz = ctx.authz_service();

Each domain defines a *ServiceExt trait for its service, keeping the access pattern consistent across ~11 domains.

Consequences

No external DI crate. Services are request-scoped (shared reference per request). The pattern is visible and greppable — ctx.modification_service() tells you exactly what's being used. Adding a new service is: construct in build_graphql_data, insert, define ext trait.

The cost is that all services are constructed at startup regardless of whether a given request needs them, and the ext traits are boilerplate (though small).