Skip to main content

Resolvers

Resolvers is the way for GQLXY to deliver your data through simple functions. There are many types of Resolvers available.

Static values​

The simplest resolvers are values set at schema construction time:

Schema schema({
.typeDefs = R"(
type Query {
user: User
}
type User {
id: ID!
name: String!
email: String!
active: Boolean!
}
)",
.resolvers = {
{"Query", Resolver{
{"user", Resolver{
{"id", "1"},
{"name", "Alice"},
{"email", "alice@example.com"},
{"active", true}
}}
}}
}
});

Function resolvers​

For dynamic data, you can use function resolver types. All receive a const ResolverArgs&:

FunctionResolver (sync)​

{"user", FunctionResolver{[&db](const ResolverArgs& args) -> ValueResolver {
auto id = args.Args()["id"].get<std::string>();
auto user = db.findUser(id);
if (!user) return std::nullopt;
return Resolver{
{"id", user->id},
{"name", user->name},
{"email", user->email}
};
}}}

AsyncFunctionResolver (std::future)​

{"user", AsyncFunctionResolver{[](const ResolverArgs& args) -> std::future<ValueResolver> {
return std::async(std::launch::async, [=]() -> ValueResolver {
return Resolver{
{"id", "1"},
{"name", "Alice"}
};
});
}}}

CoroutineResolver (C++20 coroutine)​

{"user", CoroutineResolver{[](const ResolverArgs& args) -> Task<ValueResolver> {
auto user = co_await fetchUserAsync(args.Args()["id"].get<std::string>());
co_return Resolver{
{"id", user.id},
{"name", user.name}
};
}}}

CallbackResolver​

{"user", CallbackResolver{[](const ResolverArgs& args, const std::function<void(const ValueResolver&)>& cb) {
fetchUserWithCallback(args.Args()["id"].get<std::string>(), [cb](const User& u) {
cb(Resolver{
{"id", u.id},
{"name", u.name}
});
});
}}}

Lists​

{"users", FunctionResolver{[](const ResolverArgs&) -> ValueResolver {
auto users = allUsers();
return users | std::views::transform([](const auto& u) -> ValueResolver {
return Resolver{
{"id", u.id},
{"name", u.name}
};
});
}}}

Mutations​

To define mutation resolvers, you can use the Mutation type. If you want to execute multiple mutations, they will be executed serially:

{"Mutation", Resolver{
{"addBook", FunctionResolver{[](const ResolverArgs& args) -> ValueResolver {
auto title = args.Args()["title"].get<std::string>();
auto author = args.Args()["author"].get<std::string>();
return Resolver{
{"id", "new-id"},
{"title", title},
{"author", author}
};
}}}
}}

Context​

GQLXY handles request-scoped state (auth tokens, DB connections, etc.) through SchemaResolveArgs::context:

struct RequestContext {
std::string userId;
};

auto result = schema.Resolve({
.query = "{ me { name } }",
.context = RequestContext{"42"}
}).get();

You can access it inside any resolver like this:

{"me", FunctionResolver{[](const ResolverArgs& args) -> ValueResolver {
auto& ctx = args.Context<RequestContext>();
return Resolver{
{"id", ctx.userId}
};
}}}

Aliases, fragments, and interfaces​

Aliases, named/inline fragments, and interface/union TypeResolver are all handled automatically. See Unions & Interfaces for type resolution.