= Vert.x Web GraphQL

Vert.x Web GraphQL extends Vert.x Web with the https://www.graphql-java.com[GraphQL-Java] library so that you can build a GraphQL server.

TIP: This is the reference documentation for Vert.x Web GraphQL.
It is highly recommended to get familiar with the GraphQL-Java API first.
You may start by reading the https://www.graphql-java.com/documentation/v18/getting-started/[GraphQL-Java documentation].

== Getting started

To use this module, add the following to the _dependencies_ section of your Maven POM file:

[source,xml,subs="+attributes"]
----
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-web-graphql</artifactId>
 <version>4.3.0</version>
</dependency>
----

Or, if you use Gradle:

[source,groovy,subs="+attributes"]
----
compile 'io.vertx:vertx-web-graphql:4.3.0'
----

== Handlers setup

=== HTTP

Create a Vert.x Web `link:../../apidocs/io/vertx/ext/web/Route.html[Route]` and a `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` for it:

[source,java]
----
GraphQL graphQL = setupGraphQLJava();

router.route("/graphql").handler(GraphQLHandler.create(graphQL));
----

The handler serves both `GET` and `POST` requests.
However, you can restrict the service to one type of HTTP method:

[source,java]
----
GraphQL graphQL = setupGraphQLJava();

router.post("/graphql").handler(GraphQLHandler.create(graphQL));
----

IMPORTANT: The `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` requires a `link:../../apidocs/io/vertx/ext/web/handler/BodyHandler.html[BodyHandler]` to read `POST` requests content.

==== Query batching

Query batching consists in posting an array instead of a single object to the GraphQL endpoint.

Vert.x Web GraphQL can handle such requests but by default the feature is disabled.
To enable it, create the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` with options:

[source,java]
----
GraphQLHandlerOptions options = new GraphQLHandlerOptions()
  .setRequestBatchingEnabled(true);

GraphQLHandler handler = GraphQLHandler.create(graphQL, options);
----

=== GraphiQL IDE

As you are building your application, testing your GraphQL queries in https://github.com/graphql/graphiql[GraphiQL] can be handy.

To do so, create a route for GraphiQL resources and a `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphiQLHandler.html[GraphiQLHandler]` for them:

[source,java]
----
GraphiQLHandlerOptions options = new GraphiQLHandlerOptions()
  .setEnabled(true);

router.route("/graphiql/*").handler(GraphiQLHandler.create(options));
----

Then browse to http://localhost:8080/graphiql/.

NOTE: The GraphiQL user interface is disabled by default for security reasons.
This is why you must configure the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphiQLHandlerOptions.html[GraphiQLHandlerOptions]` to enable it.

TIP: GraphiQL is enabled automatically when Vert.x Web runs in development mode.
To switch the development mode on, use the `VERTXWEB_ENVIRONMENT` environment variable or `vertxweb.environment` system property and set it to `dev`.
In this case, create the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphiQLHandler.html[GraphiQLHandler]` without changing the `enabled` property.

If your application is protected by authentication, you can customize the headers sent by GraphiQL dynamically:

[source,java]
----
graphiQLHandler.graphiQLRequestHeaders(rc -> {
  String token = rc.get("token");
  return MultiMap.caseInsensitiveMultiMap().add("Authorization", "Bearer " + token);
});

router.route("/graphiql/*").handler(graphiQLHandler);
----

Please refer to the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphiQLHandlerOptions.html[GraphiQLHandlerOptions]` documentation for further details.

=== GraphQL over WebSocket

Vert.x Web GraphQL is compatible with the https://github.com/enisdenjo/graphql-ws[GraphQL over Websocket protocol].

The websocket transport is specially useful when you need to add subscriptions to your GraphQL schema, but you can also use it for queries and mutations.

NOTE: By default, the configuration does not include a default `Origin` property. To prevent Cross-Site WebSocket
Hijacking attacks from web browsers, it is recommended to set this property to the internet facing origin of the
application. This will enforce a check that web sockets origin is from this application. This check is important because
WebSockets are not restrained by the same-origin policy, an attacker can easily initiate a WebSocket request from a
malicious webpage targeting the `ws://` or `wss://` endpoint URL of the GraphQL WS handler.

[source,java]
----
router.route("/graphql").handler(GraphQLWSHandler.create(graphQL));
----

[IMPORTANT]
====
The client will ask for the `graphql-transport-ws` websocket subprotocol.
Consequently, it has to be added to the list of supported subprotocols in the server configuration:

[source,java]
----
HttpServerOptions httpServerOptions = new HttpServerOptions()
  .addWebSocketSubProtocol("graphql-transport-ws");
----
====

To support both HTTP and Websockets on the same URI, the `link:../../apidocs/io/vertx/ext/web/handler/graphql/ws/GraphQLWSHandler.html[GraphQLWSHandler]` must be installed to the `link:../../apidocs/io/vertx/ext/web/Router.html[Router]` before the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]`:

[source,java]
----
router.route("/graphql")
  .handler(GraphQLWSHandler.create(graphQL))
  .handler(GraphQLHandler.create(graphQL));
----

IMPORTANT: A _subscription_ `DataFetcher` has to return a `org.reactivestreams.Publisher` instance.

=== Apollo WebSocketLink

You can use an https://www.apollographql.com/docs/link/links/ws/[Apollo WebSocketLink] which connects over a websocket.
This is specially useful if you want to add subscriptions to your GraphQL schema, but you can also use the websocket for queries and mutations.

[source,java]
----
GraphQL graphQL = setupGraphQLJava();

router.route("/graphql").handler(ApolloWSHandler.create(graphQL));
----

IMPORTANT: To support the `graphql-ws` websocket subprotocol, it has to be added to the server configuration:

[source,java]
----
HttpServerOptions httpServerOptions = new HttpServerOptions()
  .addWebSocketSubProtocol("graphql-ws");
vertx.createHttpServer(httpServerOptions)
  .requestHandler(router)
  .listen(8080);
----

NOTE: If you want to support a WebSocketLink and a HttpLink in the same path, you can add the ApolloWSHandler in first place and then the GraphQLHandler.

[source,java]
----
GraphQL graphQL = setupGraphQLJava();

router.route("/graphql").handler(ApolloWSHandler.create(graphQL));
router.route("/graphql").handler(GraphQLHandler.create(graphQL));
----

Here you can find how to configure the Apollo SubscriptionClient: https://github.com/apollographql/subscriptions-transport-ws

IMPORTANT: A subscription `DataFetcher` has to return a `org.reactivestreams.Publisher` instance.

== Fetching data

The GraphQL-Java API is very well suited for the asynchronous world: the asynchronous execution strategy is the default for queries (serial asynchronous for mutations).

To https://vertx.io/docs/vertx-core/java/#golden_rule[avoid blocking the event loop], all you have to do is implement https://www.graphql-java.com/documentation/v18/data-fetching/[data fetchers] that return a `CompletionStage` instead of the result directly.

[source,java]
----
DataFetcher<CompletionStage<List<Link>>> dataFetcher = environment -> {

  CompletableFuture<List<Link>> completableFuture = new CompletableFuture<>();

  retrieveLinksFromBackend(environment, ar -> {
    if (ar.succeeded()) {
      completableFuture.complete(ar.result());
    } else {
      completableFuture.completeExceptionally(ar.cause());
    }
  });

  return completableFuture;
};

RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
  .type("Query", builder -> builder.dataFetcher("allLinks", dataFetcher))
  .build();
----

=== Working with callback-based APIs

Implementing a data fetcher that returns a `CompletionStage` is not a complex task.
But when you work with Vert.x callback-based APIs, it requires a bit of boilerplate.

This is where the `link:../../apidocs/io/vertx/ext/web/handler/graphql/schema/VertxDataFetcher.html[VertxDataFetcher]` can help:

[source,java]
----
VertxDataFetcher<List<Link>> dataFetcher = VertxDataFetcher.create((env, promise) -> {
  retrieveLinksFromBackend(env, promise);
});

RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
  .type("Query", builder -> builder.dataFetcher("allLinks", dataFetcher))
  .build();
----

=== Working with Vert.x Futures

`link:../../apidocs/io/vertx/ext/web/handler/graphql/schema/VertxDataFetcher.html[VertxDataFetcher]` can also help with futurized APIs:

[source,java]
----
VertxDataFetcher<List<Link>> dataFetcher = VertxDataFetcher.create(environment -> {
  Future<List<Link>> future = retrieveLinksFromBackend(environment);
  return future;
});

RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
  .type("Query", builder -> builder.dataFetcher("allLinks", dataFetcher))
  .build();
----

=== Providing data fetchers with some context

Very often, the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` will be declared after other route handlers.
For example, you could protect your application with authentication.

In this case, it is likely that your data fetchers will need to know which user is logged-in to narrow down the results.
Let's say your authentication layer stores a `User` object in the `link:../../apidocs/io/vertx/ext/web/RoutingContext.html[RoutingContext]`.

You may retrieve this object by inspecting the `DataFetchingEnvironment`:

[source,java]
----
VertxDataFetcher<List<Link>> dataFetcher = VertxDataFetcher.create((environment, promise) -> {

  RoutingContext routingContext = GraphQLHandler.getRoutingContext(environment.getGraphQlContext());

  User user = routingContext.get("user");

  retrieveLinksPostedBy(user, promise);

});
----

NOTE: The routing context is available with any kind of data fetchers, not just `link:../../apidocs/io/vertx/ext/web/handler/graphql/schema/VertxDataFetcher.html[VertxDataFetcher]`.

=== JSON data results

The default GraphQL data fetcher is `PropertyDataFetcher`.
As a consequence, it will be able to read the fields of your domain objects without further configuration.

Nevertheless, some Vert.x data clients return `link:../../apidocs/io/vertx/core/json/JsonArray.html[JsonArray]` and `link:../../apidocs/io/vertx/core/json/JsonObject.html[JsonObject]` results.

If you don't need (or don't wish to) use a domain object layer, you can configure GraphQL-Java to use `link:../../apidocs/io/vertx/ext/web/handler/graphql/schema/VertxPropertyDataFetcher.html[VertxPropertyDataFetcher]` instead:

[source,java]
----
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();

builder.wiringFactory(new WiringFactory() {

  @Override
  public DataFetcher<Object> getDefaultDataFetcher(FieldWiringEnvironment environment) {

    return VertxPropertyDataFetcher.create(environment.getFieldDefinition().getName());

  }
});
----

TIP: `link:../../apidocs/io/vertx/ext/web/handler/graphql/schema/VertxPropertyDataFetcher.html[VertxPropertyDataFetcher]` wraps a `PropertyDataFetcher` so you can still use it with domain objects.

=== Batch loading

Dataloaders help you to load data efficiently by batching fetch requests and caching results.

First, create a batch loader:

[source,java]
----
BatchLoaderWithContext<String, Link> linksBatchLoader = (ids, env) -> {
  // retrieveLinksFromBackend takes a list of ids and returns a CompletionStage for a list of links
  return retrieveLinksFromBackend(ids, env);
};
----

Then, configure the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` to create a `DataLoaderRegistry` for each request:

[source,java]
----
GraphQLHandler handler = GraphQLHandler.create(graphQL).beforeExecute(builderWithContext -> {

  DataLoader<String, Link> linkDataLoader = DataLoaderFactory.newDataLoader(linksBatchLoader);

  DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry().register("link", linkDataLoader);

  builderWithContext.builder().dataLoaderRegistry(dataLoaderRegistry);

});
----

If you work with Vert.x APIs, you may use a `link:../../apidocs/io/vertx/ext/web/handler/graphql/dataloader/VertxBatchLoader.html[VertxBatchLoader]` or a `link:../../apidocs/io/vertx/ext/web/handler/graphql/dataloader/VertxMappedBatchLoader.html[VertxMappedBatchLoader]` to simplify your code:

[source,java]
----
BatchLoaderWithContext<Long, String> commentsBatchLoader = VertxBatchLoader.create((ids, env) -> {
  // findComments takes a list of ids and returns a Future for a list of links
  return findComments(ids, env);
});
----

=== File uploads

https://github.com/jaydenseric/graphql-multipart-request-spec[GraphQL multipart request] is an interoperable multipart form field structure for `GraphQL` requests.
By enabling this functionality, GraphQL clients will be able to upload files using a single mutation call.
All the server-side file handling will be abstracted by the `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]`.

To enable it, create a `link:../../apidocs/io/vertx/ext/web/handler/graphql/GraphQLHandler.html[GraphQLHandler]` with the requestMultipartEnabled configuration set to true and add the `link:../../apidocs/io/vertx/ext/web/handler/BodyHandler.html[BodyHandler]` to the router.

[source,java]
----
GraphQLHandler graphQLHandler = GraphQLHandler.create(
  setupGraphQLJava(),
  new GraphQLHandlerOptions().setRequestMultipartEnabled(true)
);

Router router = Router.router(vertx);

router.route().handler(BodyHandler.create());
router.route("/graphql").handler(graphQLHandler);
----

IMPORTANT: If the router does not have a `link:../../apidocs/io/vertx/ext/web/handler/BodyHandler.html[BodyHandler]`, the multipart request parser will not be able to handle the GraphQL mutation call.

Finally, create the `Upload` scalar and set it to the `RuntimeWiring`:

[source,java]
----
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().scalar(UploadScalar.build()).build();
----

The `FileUpload` instance can be accessed using the `DataFetchingEnvironment::getArgument` method.

[source,java]
----
FileUpload file = environment.getArgument("myFile");
----

ifeval::["java" == "java"]
include::override/rxjava3.adoc[]
endif::[]