Maven project descriptor
In this guide you will learn how to create a plugin for Gentics Mesh which will extend the GraphQL API in order query a small library inventory.

This example is inspired by the GraphQL Java Getting Started Guide.
All sources for this example are available on GitHub.
Before we can start you need to install Apache Maven and a Java Development Kit.
Plugin Code:
pom.xml
Maven project descriptor
src/main/resources/schema.graphqls
GraphQL Schema
src/main/java/com/gentics/mesh/plugin/GraphQLDataFetchers.java
Data fetchers for the inventory data
src/main/java/com/gentics/mesh/plugin/GraphQLExamplePlugin.java
The actual plugin implementation
Test Code:
src/test/java/com/gentics/mesh/plugin/GraphQlExamplePluginTest.java
Plugin integration test
src/test/java/com/gentics/mesh/plugin/PluginRunnerExample.java
Example runner which will start Mesh and the plugin
/pom.xml
The first thing we need to do is to setup our project. In this example we’ll use the pom.xml file which is used by Apache Maven to build the plugin project.
The pom.xml will also contain plugin metadata. It is the place where the plugin manifest will be specified that is later added to the final .jar file.
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.gentics.mesh.plugin</groupId>
  <artifactId>mesh-graphql-library-example-plugin</artifactId>
  <parent>
    <groupId>com.gentics.mesh.plugin.examples</groupId>
    <artifactId>mesh-plugin-examples</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- We use these properties to specify the plugin manifest -->
    <plugin.id>library</plugin.id>
    <plugin.name>The GraphQL example plugin</plugin.name>
    <plugin.description>A very simple plugin which shows how to extend the
      GraphQL API.</plugin.description>
    <plugin.class>com.gentics.mesh.plugin.GraphQLExamplePlugin</plugin.class>
    <plugin.version>${project.version}</plugin.version>
    <plugin.license>Apache License 2.0</plugin.license>
    <plugin.author>Joe Doe</plugin.author>
    <plugin.inception>2019-07-08</plugin.inception>
  </properties>
  <dependencies>
    <!-- The mesh plugin api dependencies.
         Please note that these dependencies need to be set to provided -->
    <dependency>
      <groupId>com.gentics.mesh</groupId>
      <artifactId>mesh-plugin-dep</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Test dependencies -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.gentics.mesh</groupId>
      <artifactId>mesh-test-common</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.gentics.mesh</groupId>
      <artifactId>mesh-core</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.gentics.mesh.plugin.examples</groupId>
      <artifactId>mesh-plugins-test-commons</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.gentics.mesh</groupId>
      <artifactId>mesh-mdm-orientdb-wrapper</artifactId>
      <scope>test</scope>
      <version>${mesh.version}</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <verbose>true</verbose>
          <source>11</source>
          <target>11</target>
        </configuration>
      </plugin>
      <!--The shade plugin will generate the jar with all the needed dependencies -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <!-- We use the transformer to add the manifest properties to the jar manifest -->
                <transformer
                  implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Plugin-Id>${plugin.id}</Plugin-Id>
                    <Plugin-Version>${plugin.version}</Plugin-Version>
                    <Plugin-Author>${plugin.author}</Plugin-Author>
                    <Plugin-Class>${plugin.class}</Plugin-Class>
                    <Plugin-Description>${plugin.description}</Plugin-Description>
                    <Plugin-License>${plugin.license}</Plugin-License>
                    <Plugin-Inception>${plugin.inception}</Plugin-Inception>
                  </manifestEntries>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <!-- The repository section is important.
       Otherwise you will not be able to download the artifacts -->
  <repositories>
    <repository>
      <id>maven.gentics.com</id>
      <name>Gentics Maven Repository</name>
      <url>https://maven.gentics.com/maven2</url>
      <releases>
        <enabled>true</enabled>
      </releases>
    </repository>
  </repositories>
</project>The schema.graphqls file contains the GraphQL Schema definition (SDL) which is used to setup the types for the inventory. It defines fields for each type. In our case we only need the Book and the Author types.
type Query {
  bookById(id: ID): Book
}
type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}
type Author {
  id: ID
  firstName: String
  lastName: String
}GraphQLExamplePlugin.java
The GraphQLExamplePlugin file contains the actual plugin code. The plugin itself is very simple.
We need to extend the AbstractPlugin class and implement the GraphQLPlugin interface since our plugin will extend the GraphQL API.
In the initialize() method we setup the GraphQLSchema for our plugin. The counterpart is the shutdown() method.
package com.gentics.mesh.plugin;
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
import java.net.URL;
import org.pf4j.PluginWrapper;
import com.gentics.mesh.plugin.env.PluginEnvironment;
import com.gentics.mesh.plugin.graphql.GraphQLPlugin;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import io.reactivex.Completable;
public class GraphQLExamplePlugin extends AbstractPlugin implements GraphQLPlugin {
	private GraphQLSchema schema;
	private GraphQLDataFetchers fetcher = new GraphQLDataFetchers();
	public GraphQLExamplePlugin(PluginWrapper wrapper, PluginEnvironment env) {
		super(wrapper, env);
	}
	@Override
	public Completable initialize() {
		return Completable.fromAction(() -> {
			URL url = Resources.getResource("schema.graphqls");
			String sdl = Resources.toString(url, Charsets.UTF_8);
			schema = buildSchema(sdl);
		});
	}
	private GraphQLSchema buildSchema(String sdl) {
		TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
		RuntimeWiring runtimeWiring = buildWiring();
		SchemaGenerator schemaGenerator = new SchemaGenerator();
		return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
	}
	private RuntimeWiring buildWiring() {
		return RuntimeWiring.newRuntimeWiring()
			.type(newTypeWiring("Query")
				.dataFetcher("bookById", fetcher.getBookByIdDataFetcher()))
			.type(newTypeWiring("Book")
				.dataFetcher("author", fetcher.getAuthorDataFetcher()))
			.build();
	}
	@Override
	public GraphQLSchema createRootSchema() {
		return schema;
	}
}The data fetcher contains the code that is used to actually retrieve the inventory information. One fetcher is used for looking up books by id. The other one will return the author.
package com.gentics.mesh.plugin;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import graphql.schema.DataFetcher;
public class GraphQLDataFetchers {
	private static List<Map<String, String>> books = Arrays.asList(
		ImmutableMap.of("id", "book-1",
			"name", "Harry Potter and the Philosopher's Stone",
			"pageCount", "223",
			"authorId", "author-1"),
		ImmutableMap.of("id", "book-2",
			"name", "Moby Dick",
			"pageCount", "635",
			"authorId", "author-2"),
		ImmutableMap.of("id", "book-3",
			"name", "Interview with the vampire",
			"pageCount", "371",
			"authorId", "author-3"));
	private static List<Map<String, String>> authors = Arrays.asList(
		ImmutableMap.of("id", "author-1",
			"firstName", "Joanne",
			"lastName", "Rowling"),
		ImmutableMap.of("id", "author-2",
			"firstName", "Herman",
			"lastName", "Melville"),
		ImmutableMap.of("id", "author-3",
			"firstName", "Anne",
			"lastName", "Rice"));
	public DataFetcher getBookByIdDataFetcher() {
		return dataFetchingEnvironment -> {
			String bookId = dataFetchingEnvironment.getArgument("id");
			return books
				.stream()
				.filter(book -> book.get("id").equals(bookId))
				.findFirst()
				.orElse(null);
		};
	}
	public DataFetcher getAuthorDataFetcher() {
		return dataFetchingEnvironment -> {
			Map<String, String> book = dataFetchingEnvironment.getSource();
			String authorId = book.get("authorId");
			return authors
				.stream()
				.filter(author -> author.get("id").equals(authorId))
				.findFirst()
				.orElse(null);
		};
	}
}For testing it is possible to use the MeshLocalServer class rule which will make it possible to start-up an in-memory Gentics Mesh server that can be used to run your plugin.
The test will:
Start Gentics Mesh in memory
Deploy the plugin
Setup an empty project
Finally run the the query to fetch the data from the plugin
package com.gentics.mesh.plugin;
import java.io.IOException;
import org.junit.ClassRule;
import org.junit.Test;
import com.gentics.mesh.core.rest.graphql.GraphQLResponse;
import com.gentics.mesh.core.rest.project.ProjectCreateRequest;
import com.gentics.mesh.rest.client.MeshRestClient;
import com.gentics.mesh.test.local.MeshLocalServer;
import io.reactivex.Completable;
import io.reactivex.Single;
/**
 * This example test demonstrates how to use the {@link MeshLocalServer} to deploy a plugin.
 */
public class GraphQlExamplePluginTest {
	private static String PROJECT = "test";
	@ClassRule
	public static final MeshLocalServer server = new OrientDBMeshLocalServer()
		.withInMemoryMode()
		.withPlugin(GraphQLExamplePlugin.class, "myPlugin")
		.waitForStartup();
	@Test
	public void testPlugin() throws IOException {
		MeshRestClient client = server.client();
		// Create the project
		Completable createProject = client.createProject(
			new ProjectCreateRequest()
				.setName(PROJECT)
				.setSchemaRef("folder"))
			.toCompletable();
		// Run query on the plugin
		String query = "{ pluginApi { myPlugin { text } } }";
		Single<GraphQLResponse> rxQuery = client.graphqlQuery(PROJECT, query)
			.toSingle();
		// Run the operations
		GraphQLResponse result = createProject.andThen(rxQuery).blockingGet();
		// Print the GraphQL API result
		System.out.println(result.getData().encodePrettily());
	}
}When running the test you should see the output in the result of the GraphQL test request.
INFO: 127.0.0.1 - POST /api/v1/test/graphql HTTP/1.1 200 106 - 12 ms
{
  "pluginApi" : {
    "myPlugin" : {
      "bookById" : {
        "id" : "book-1",
        "name" : "Harry Potter and the Philosopher's Stone"
      }
    }
  }
}Now we can package the plugin by running mvn clean package. The built plugin will be placed in the target directory.
We can now place the mesh-graphql-library-example-plugin-0.0.1-SNAPSHOT.jar file in the Gentics Mesh plugins directory.
You can now deploy start a new Gentics Mesh instance which will deploy the plugin during start-up:
docker run --rm \
    -v $PWD/target/mesh-graphql-library-example-plugin-0.0.1-SNAPSHOT.jar:/plugins/graphql.jar \
    -p 8080:8080 \
    gentics/mesh:0.38.0It is also possible to deploy a plugin in a running instance via the /admin/plugins endpoint.
With an admin token at hand you can invoke the deployment via cURL:
curl -X POST \
 -d '{
    "path":"mesh-graphql-library-example-plugin-0.0.1-SNAPSHOT.jar"
    }' \
 -H "Authorization: Bearer $MESH_TOKEN" \
 -H "Content-Type: application/json" \
 $MESH_BASE/admin/pluginsYou can read more about how to use the REST API with cURL in the API guide.
Once the plugin has been deployed you can use the plugin via the GraphQL endpoint.
curl -X POST \
 -d '{
    "query":"{ pluginApi { myPlugin { bookById(id: \"book-1\") {id name } } } }"
    }' \
 -H "Content-Type: application/json" \
 $MESH_BASE/test/graphql