GraphQL
Punk::GraphQL mounts a GraphQL-over-HTTP endpoint into the application, executed by GraphQL::Houtou - an XS-first parser and native-VM runtime. The schema compiles once at boot, in the server parent, so the compiled artifacts are shared copy-on-write across preforked workers.
package MyApp;
use Punk;
use Punk::Plugin::GraphQL;
plugin 'GraphQL';
graphql '/graphql' => 'schema/app.graphql', {
resolvers => {
Query => 'Books', # MyApp::Controller::Books
Mutation => { rename => 'Books#rename' },
},
context => 'Books#context',
guard => 'Auth#user',
graphiql => 1,
};
The schema is a filename, an SDL string, or an already built
GraphQL::Houtou::Schema. Multiple graphql declarations mount
independent endpoints, each with its own schema and runtime.
Resolvers
Resolvers take the same targets as routes - a coderef or a
'Controller#method' string - so a typo croaks at boot, naming the
class and method. A whole type may name a controller class: every
field the SDL declares on that type which has a same-named method on
the class is wired to it, and fields without one keep the default
resolver.
Resolver methods use Houtou's signature -
($source, $args, $context, $info) - not a route handler's ($c).
The request crosses over through the context builder, called once
per request with the Punk::Context:
sub context {
my ($c) = @_;
return ({ db => $c->db });
}
Its return is list-assigned as (context, on_stall) - the optional
second value is the batching hook that flushes
GraphQL::Houtou::DataLoader instances, which you create inside the
builder so their caches are request-scoped.
The request path
Punk's C dispatcher matches the route, the envelope is decoded through
the File::Raw::JSON C ABI, Houtou executes on its XS VM and renders
straight to UTF-8 JSON bytes, and Punk's C finish path passes them
through without re-encoding. guard takes an ordinary Punk guard, and
graphiql => 1 serves the in-browser IDE on GET.