Skip to content

GraphQL

Write a resolver as a struct and decorate it with #[resolver]; its operations register themselves into the schema at boot — there is no central queries = [...] list to keep in sync. Root resolvers (#[query], #[mutation]) and entity-field resolvers (#[field_resolver]) live on the same impl block. The schema composes itself from a link-time registry, the playground self-mounts in dev, and the SDL is committed.

nest-rs-graphql builds on async-graphql (served through async-graphql-poem) — the framework wraps it so resolvers register themselves and inject providers like any other struct.

Terminal window
cargo add nest-rs-graphql
src/greeting/resolver.rs
use nest_rs_graphql::resolver;
#[resolver]
pub struct GreetingResolver;
#[resolver]
impl GreetingResolver {
#[query]
#[public]
async fn greeting(&self, name: Option<String>) -> String {
format!("Hello, {}!", name.as_deref().unwrap_or("World"))
}
}
  • #[resolver] on the struct registers it with the schema discovery registry.
  • #[resolver] on the impl block orchestrates the operations within.
  • #[query] declares a top-level query. The method name becomes the field name (camelCased on the wire: greeting).
  • #[public] declares the operation’s access posture — deliberately ungated. Every #[query] / #[mutation] carries either #[public] or #[authorize(Action, Entity)] (ability gate + automatic response masking); a method with neither does not compile.
  • Arguments are mapped to GraphQL input arguments by serde. Option<T> becomes nullable.

#[query], #[mutation], #[field_resolver], #[public], #[authorize] and #[inject] are inner attributes the #[resolver] macro consumes in place — there is nothing to import for them. Only resolver (and crud / dataloader on the pages that use them) comes from nest_rs_graphql.

Mount the GraphQL transport once at the app root:

src/module.rs
use nest_rs_core::module;
use nest_rs_graphql::GraphqlModule;
use crate::greeting::GreetingModule;
#[module(imports = [GreetingModule, GraphqlModule::for_root(None)])]
pub struct GreetingsAppModule;
src/greeting/module.rs
use nest_rs_core::module;
use super::resolver::GreetingResolver;
#[module(providers = [GreetingResolver])]
pub struct GreetingModule;

GraphqlModule::for_root mounts POST /graphql (the endpoint) and, when the playground is on, GET /graphql for the interactive client.

Terminal window
$ curl -sX POST http://localhost:3000/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ greeting(name: \"Ada\") }"}'
{"data":{"greeting":"Hello, Ada!"}}

In dev (NESTRS_GRAPHQL__PLAYGROUND=true), open http://localhost:3000/graphql for the playground.

A resolver injects providers like any other struct:

use std::sync::Arc;
use nest_rs_graphql::resolver;
use crate::greeting::service::GreetingService;
#[resolver]
pub struct GreetingResolver {
#[inject]
svc: Arc<GreetingService>,
}
#[resolver]
impl GreetingResolver {
#[query]
#[public]
async fn greeting(&self, name: Option<String>) -> String {
self.svc.greet(name.as_deref().unwrap_or("World"))
}
}

The container resolves GreetingService from the import tree. No container handle to thread, no factory to call.

The GraphQL surface fans out from here. Each page covers one concern:

  • Queries and mutations#[query], #[mutation], inputs, outputs, returning typed entities.
  • Field resolvers — custom computed fields with #[field_resolver], and the one ComplexObject caveat to watch for.
  • Relations resolve themselves — declare a SeaORM relation, get a typed GraphQL field with a batched loader for free.
  • Dataloaders#[dataloader] on a service method for custom batched fetches, beyond what relations give you.
  • Errorsasync_graphql::Error, extensions, and mapping ServiceError to a GraphQL envelope.
  • Subscriptions — currently not supported; the page documents the path forward.
  • ConfigurationGraphqlConfig, SDL emission, playground toggle, mount path.
  • Security — bind a GraphqlAuthGuard so resolvers run with an authenticated principal and an ambient Ability.
  • Database — the data layer that the relation and dataloader pages build on.
  • crates/features/src/users/graphql/resolver.rs — a production-grade resolver with relations, dataloaders and authorization.
  • posts/graphql/resolver.rs in the demo — the publishPost mutation and draft/published status.
  • crates/nest-rs-graphql/#[resolver], #[query], #[mutation], #[field_resolver], schema composition.
  • crates/nest-rs-resource/#[expose] for an entity that becomes a GraphQL type and an OpenAPI schema from one declaration.