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.
Install
Section titled “Install”cargo add nest-rs-graphqlThe minimal resolver
Section titled “The minimal resolver”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:
use nest_rs_core::module;use nest_rs_graphql::GraphqlModule;use crate::greeting::GreetingModule;
#[module(imports = [GreetingModule, GraphqlModule::for_root(None)])]pub struct GreetingsAppModule;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.
Run it
Section titled “Run it”$ 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.
Injecting a service
Section titled “Injecting a service”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.
In this section
Section titled “In this section”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 oneComplexObjectcaveat 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. - Errors —
async_graphql::Error, extensions, and mappingServiceErrorto a GraphQL envelope. - Subscriptions — currently not supported; the page documents the path forward.
- Configuration —
GraphqlConfig, SDL emission, playground toggle, mount path.
Going further
Section titled “Going further”- Security — bind a
GraphqlAuthGuardso resolvers run with an authenticated principal and an ambientAbility. - Database — the data layer that the relation and dataloader pages build on.
Reference
Section titled “Reference”crates/features/src/users/graphql/resolver.rs— a production-grade resolver with relations, dataloaders and authorization.posts/graphql/resolver.rsin the demo — thepublishPostmutation 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.