Skip to content

neo1ite/Mojolicious-Plugin-GraphQL

 
 

Repository files navigation

NAME

Mojolicious::Plugin::GraphQL - a plugin for adding GraphQL route handlers

SYNOPSIS

my $schema = GraphQL::Schema->from_doc(<<'EOF');
schema {
  query: QueryRoot
}
type QueryRoot {
  helloWorld: String
}
EOF

# for Mojolicious substitute "plugin" with $app->plugin(...
# Mojolicious::Lite (with endpoint under "/graphql")
plugin GraphQL => {
  schema => $schema, root_value => { helloWorld => 'Hello, world!' }
};

# OR, equivalently:
plugin GraphQL => {schema => $schema, handler => sub {
  my ($c, $body, $execute, $subscribe_fn) = @_;
  # returns JSON-able Perl data
  $execute->(
    $schema,
    $body->{query},
    { helloWorld => 'Hello, world!' }, # $root_value
    $c->req->headers,
    $body->{variables},
    $body->{operationName},
    undef, # $field_resolver
    $subscribe_fn ? (undef, $subscribe_fn) : (), # only passed for subs
  );
}};

# OR, with bespoke user-lookup and caching:
plugin GraphQL => {schema => $schema, handler => sub {
  my ($c, $body, $execute, $subscribe_fn) = @_;
  my $user = MyStuff::User->lookup($app->request->headers->header('X-Token'));
  die "Invalid user\n" if !$user; # turned into GraphQL { errors => [ ... ] }
  my $cached_result = MyStuff::RequestCache->lookup($user, $body->{query});
  return $cached_result if $cached_result;
  MyStuff::RequestCache->cache_and_return($execute->(
    $schema,
    $body->{query},
    undef, # $root_value
    $user, # per-request info
    $body->{variables},
    $body->{operationName},
    undef, # $field_resolver
    $subscribe_fn ? (undef, $subscribe_fn) : (), # only passed for subs
  ));
};

# With GraphiQL, on /graphql
plugin GraphQL => {schema => $schema, graphiql => 1};

plugin GraphQL => {
  schema => $schema,
  graphiql => {
    enable       => 1,
    mode         => 'esm', # esm | legacy_umd
    graphiql_ver => '5.2.2',
    react_ver    => '19.1.0',
    graphql_ver  => '16.11.0',
  },
};

DESCRIPTION

This plugin allows you to easily define a route handler implementing a GraphQL endpoint, including a websocket for subscriptions following Apollo's subscriptions-transport-ws protocol.

As of version 0.09, it will supply the necessary promise_code parameter to "execute" in GraphQL::Execution. This means your resolvers can (and indeed should) return Promise objects to function asynchronously. As of 0.15 these must be "Promises/A+" as subscriptions require resolve and reject methods.

The route handler code will be compiled to behave like the following:

  • Passes to the GraphQL execute, possibly via your supplied handler, the given schema, $root_value and $field_resolver. Note as above that the wrapper used in this plugin will supply the hash-ref matching "PromiseCode" in GraphQL::Type::Library.
  • The action built matches POST / GET requests.
  • Returns GraphQL results in JSON form.

OPTIONS

Mojolicious::Plugin::GraphQL supports the following options.

convert

Array-ref. First element is a classname-part, which will be prepended with "GraphQL::Plugin::Convert::". The other values will be passed to that class's "to_graphql" in GraphQL::Plugin::Convert method. The returned hash-ref will be used to set options, particularly schema, and probably at least one of resolver and root_value.

endpoint

String. Defaults to /graphql.

schema

A GraphQL::Schema object. As of 0.15, must be supplied.

root_value

An optional root value, passed to top-level resolvers.

resolver

An optional field resolver, replacing the GraphQL default.

handler

An optional route-handler, replacing the plugin's default - see example above for possibilities.

It must return JSON-able Perl data in the GraphQL format, which is a hash with at least one of a data key and/or an errors key.

If it throws an exception, that will be turned into a GraphQL-formatted error.

If being used for a subscription, it will be called with a fourth parameter as shown above. It is safe to not handle this if you are content with GraphQL's defaults.

graphiql

Controls whether requesting the endpoint with Accept: text/html will return the GraphiQL user interface. Defaults to false.

If the request includes a raw query parameter, the GraphiQL user interface is skipped and the endpoint behaves as a normal GraphQL endpoint even when Accept: text/html is sent.

May be set to a true value for the default behaviour, or a hash-ref for more control.

# Mojolicious::Lite
plugin GraphQL => {schema => $schema, graphiql => 1};

plugin GraphQL => {
  schema => $schema,
  graphiql => {
    enable       => 1,
    mode         => 'esm', # esm | legacy_umd
    graphiql_ver => '5.2.2',
    react_ver    => '19.1.0',
    graphql_ver  => '16.11.0',
  },
};

If the value is a hash-ref, the following keys are recognised:

  • enable

    Boolean, default true.

  • mode

    Either esm or legacy_umd. If omitted, defaults to esm for non-subscription schemas and legacy_umd for subscription schemas.

  • title

    Page title. Defaults to GraphiQL.

  • graphiql_ver, react_ver, graphql_ver

    Pinned versions for the ESM GraphiQL UI.

  • graphiql_react_ver, graphiql_toolkit_ver

    Pinned versions for ESM helper packages.

  • legacy_graphiql_ver, legacy_react_ver, legacy_graphql_ver

    Pinned versions for the legacy UMD GraphiQL UI.

The ESM UI currently covers queries and mutations. Subscription support in the bundled GraphiQL UI remains on the legacy UMD path for now.

keepalive

Defaults to 0, which means do not send. Otherwise will send a keep-alive packet over websocket every specified number of seconds.

METHODS

Mojolicious::Plugin::GraphQL inherits all methods from Mojolicious::Plugin and implements the following new ones.

register

my $route = $plugin->register(Mojolicious->new, {schema => $schema});

Register renderer in Mojolicious application.

make_code_closure

my $handler = make_code_closure($schema, $root_value, $field_resolver);

Returns the default handler closure used by the plugin when handler is not provided.

The returned code reference executes the GraphQL operation against the given schema, using the supplied root value and optional field resolver.

This is mainly useful for advanced integrations where you want to reuse the plugin's default execution behavior but still wrap or compose it with your own logic.

EXPORTS

Exportable is the function promise_code, which returns a hash-ref suitable for passing as the 8th argument to "execute" in GraphQL::Execution.

SUBSCRIPTIONS

To use subscriptions within your web app, just insert this JavaScript:

<script src="//unpkg.com/subscriptions-transport-ws@0.9.16/browser/client.js"></script>
# ...
const subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient(websocket_uri, {
  reconnect: true
});
subscriptionsClient.request({
  query: "subscription s($c: [String!]) {subscribe(channels: $c) {channel username dateTime message}}",
  variables: { c: channel },
}).subscribe({
  next(payload) {
    var msg = payload.data.subscribe;
    console.log(msg.username + ' said', msg.message);
  },
  error: console.error,
});

Note the use of parameterised queries, where you only need to change the variables parameter. The above is adapted from the sample app, https://github.com/graphql-perl/sample-mojolicious.

When GraphiQL is enabled, subscription schemas default to the bundled legacy UMD UI for compatibility with Apollo's subscriptions-transport-ws protocol.

SEE ALSO

GraphQL

GraphQL::Plugin::Convert

https://github.com/apollographql/subscriptions-transport-ws#client-browser - Apollo documentation

AUTHOR

Ed J

Based heavily on Mojolicious::Plugin::PODRenderer.

COPYRIGHT AND LICENSE

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

About

Mojolicious plugin to make GraphQL endpoints

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages

  • Perl 99.5%
  • Dockerfile 0.5%