Use the GraphQL Manifold for productive Schema-First GraphQL development in any Java project.
Type-safely build and execute queries and mutations without introducing a code generation step in your build
process. Access GraphQL types defined in standard .graphql
schemas directly in your Java code. Build queries
using native .graphql
query files and immediately access changes as you make them from Java code -- without
recompiling! Your code is always in sync with GraphQL definitions.
Clone the sample GraphQL application to quickly begin experimenting with GraphQL using Manifold.
- GraphQL Files
- Fluent API
- Creating Types & Queries
- Execute Queries
- Execute Mutations
- HTTP Request Configuration
- Configuring Proxies
- Loading a GraphQL Object
- Writing GraphQL Objects
- Copying GraphQL Objects
- Types
- Scalar Types
- Configuring for Multiple Schemas
- Inlining Queries with Fragments
- IDE Support
- Setup
- Javadoc
- License
- Versioning
- Author
The GraphQL Manifold enables your Java code to directly access types and queries defined in native GraphQL schema files.
Drop a schema file into your project and start using and building queries in your code, no code generators to engage, no
recompiling between schema changes. Supports all standard file extensions including .graphql
, .graphqls
, and .gql
.
When you add a GraphQL file to your project it becomes a Java resource. A resource, like a class, is rooted in a resource or
source directory, depending on your build configuration. Maven and Gradle projects, for example, typically define a
src/main/resources
root for all the resource files in a given module.
The path from the resource root determines the fully qualified name of the types derived from the GraphQL files. For
example, GraphQL type definitions from file src/main/resources/com/example/Movies.graphql
are accessible from Java
interface com.example.Movies
where com.example
is the package and Movies
is the top-level interface name. Type
definitions are inner classes defined inside the Movies
interface.
You can provide any number of GraphQL resource files; all files form a collective GraphQL type domain. This means you can organize schema types in separate files and also separate queries and mutations from schema definitions.
type Query {
movies(genre: Genre!, title: String, releaseDate: Date) : [Movie!]
reviews(genre: Genre) : [Review!]
}
type Movie {
id: ID!
title: String!
genre: [Genre!]!
releaseDate: Date!
}
type Mutation {
createReview(movie: ID!, review: ReviewInput) : Review
}
input MovieInput {
title: String!
genre: Genre!
releaseDate: Date
}
input ReviewInput {
stars: Int!
comment: String
}
type Review {
id: ID!
movie: Movie!
stars: Int!
comment: String
}
enum Genre {
Action, Comedy, Drama, Fantasy, Horror, Romance, SciFi, Western
}
scalar Date
query MovieQuery($genre: Genre!, $title: String, $releaseDate: Date) {
movies(genre: $genre, title: $title, releaseDate: $releaseDate) {
id
title
genre
releaseDate
}
}
query ReviewQuery($genre: Genre) {
reviews(genre: $genre) {
id
stars
comment
movie {
id
title
}
}
}
mutation ReviewMutation($movie: ID!, $review: ReviewInput!) {
createReview(movie: $movie, review: $review) {
id
stars
comment
}
}
extend type Query {
reviewsByStars(stars: Int) : [Review!]!
}
GraphQL is a language-neutral, type-safe API. The GraphQL Manifold provides a concise, fluent mapping of the API to
Java. For example, the MovieQuery
type is a Java interface and provides type-safe methods to:
- create a
MovieQuery
- build a
MovieQuery
- modify properties of a
MovieQuery
- load a
MovieQuery
from a string, a file, or a URL - execute a
MovieQuery
with type-safe response - write a
MovieQuery
as formatted JSON, YAML, or XML - copy a
MovieQuery
- cast to
MovieQuery
from any structurally compatible type includingMap
s, all without proxies
You create an instance of a GraphQL type using either the create()
method or the builder()
method.
The create()
method defines parameters matching the non-null
parameters declared in the query schema; if no non-null
parameters exist, create()
has an empty parameter list.
For example, the MovieQuery.create()
method declares one parameter corresponding with the non-null Genre
parameter:
static MovieQuery create(@NotNull Genre genre) {...}
You can use this to create a new MovieQuery
with a Genre
then modify it using setter methods to change optional
properties:
import com.example.MovieQueries.*;
import java.time.LocalDate;
import static com.example.Movies.Genre.Action;
...
MovieQuery query = MovieQuery.create(Action);
query.setTitle("Le Mans");
query.setReleaseDate(LocalDate.of(1971, 6, 3));
Alternatively, you can use builder()
to fluently build a new instance:
MovieQuery query = MovieQuery.builder(Action)
.withTitle("Le Mans")
.withReleaseDate(LocalDate.of(1971, 6, 3))
.build();
You can initialize several properties in a chain of with
calls in the builder. This saves a bit of typing with
heavier APIs. After it is fully configured call the build()
method to construct the type.
You can execute queries and mutations using a concise, fluent API. Simply provide the endpoint as a URL, and get or post your request. Query results are type-safe and constrained to the properties defined in the GraphQL query.
import com.example.MovieQueries.*;
import java.time.LocalDate;
import static com.example.Movies.Genre.Action;
...
private static String ENDPOINT = "http://com.example/graphql";
...
var query = MovieQuery.builder(Action).build();
var result = query.request(ENDPOINT).post();
var actionMovies = result.getMovies();
for (var movie : actionMovies) {
out.println(
"Title: " + movie.getTitle() + "\n" +
"Genre: " + movie.getGenre() + "\n" +
"Year: " + movie.getReleaseDate().getYear() + "\n");
}
Pro Tip: Include manifold-props in your project for more concise usage of get/set property methods:
out.println( "Title: " + movie.title + "\n" + "Genre: " + movie.genre + "\n" + "Year: " + movie.releaseDate.year + "\n");Configure dependencies like this:
implementation 'systems.manifold:manifold-props-rt:2025.1.1' annotationProcessor 'systems.manifold:manifold-props:2025.1.1'See complete setup docs for details.
You execute a mutation exactly as you would a query using the same API. Note this example creates a ReviewInput
instance also using the same API.
// Find the movie to review ("Le Mans")
var movie = MovieQuery.builder(Action).withTitle("Le Mans").build()
.request(ENDPOINT).post().getMovies().first();
// Submit a review for the movie
var review = ReviewInput.builder(5).withComment("Topnotch racing film.").build();
var mutation = ReviewMutation.builder(movie.getId(), review).build();
var createdReview = mutation.request(ENDPOINT).post().getCreateReview();
out.println(
"Review for: " + movie.getTitle() + "\n" +
"Stars: " + createdReview.getStars() + "\n" +
"Comment: " + createdReview.getComment() + "\n"
);
You can configure the HTTP request to your needs. For instance, you can use a variety of authorization options, set header values, specify a timeout, etc.
// Specify an authorization token
query.request(ENDPOINT).withAuthorization(...)
// Supply header values
query.request(ENDPOINT).withHeader(...)
// Set a timeout
query.request(ENDPOINT).withTimeout(...)
If your endpoint involves a proxy server, you can setup an Endpoint
class.
private static final Endpoint ENDPOINT =
new Endpoint("https://example.com/graphql", "http://my.proxy.com", 4321);
...
var result = query.request(ENDPOINT).post();
An Endpoint
lets you specify the GraphQL endpoint URL along with a proxy server address, port, and type. Simpler
constructors allow you to omit the port and/or type, which default to 8080
and HTTP
. You can also use Endpoint
without a proxy using just a URL.
In addition to creating an object from scratch with create()
and build()
you can also load an instance from
a variety of existing sources using load()
.
You can load a MovieQuery
instance from a variety of formats including JSON, XML, and YAML:
MovieQuery query = MovieQuery.load().fromYaml(
"genre: Action\n" +
"title: Le Mans\n" +
"releaseDate: 1971-06-03");
Load from a file:
User user = User.load().fromJsonFile("/path/to/MyMovieQuery.json");
An instance of a GraphQL object can be written as formatted text with write()
:
toJson()
- produces a JSON formatted StringtoYaml()
- produces a YAML formatted StringtoXml()
- produces an XML formatted String
The following example produces a JSON formatted string:
MovieQuery query = MovieQuery.builder(Action)
.withTitle("Le Mans")
.withReleaseDate(LocalDate.of(1972, 6, 3))
.build();
String json = query.write().toJson();
System.out.println(json);
Output:
{
"genre": "Action",
"title": "Le Mans",
"releaseDate": "1971-06-03"
}
Use the copy()
method to make a deep copy of any GraphQL object:
MovieQuery query = MovieQuery.create(...);
...
MovieQuery copy = query.copy();
Alternatively, you can use the copier()
static method for a richer set of features:
MovieQuery copy = MovieQuery.copier(query).withGenre(Drama).build();
copier()
is a lot like builder()
but lets you start with an already built object you can modify.
GraphQL provides several useful type abstractions these include:
schema
type
input
interface
enum
union
scalar
fragment
query
mutation
subscription
extend
The GraphQL manifold supports all type abstractions except the subscription type, which will be supported in a later release.
schema
is a simple type that lets you specify the root query type, the root mutation type, and the root subscription
type. Without a schema type, the default root type names are Query
, Mutation
, and Subscription
.
type
is the GraphQL foundational abstraction. The manifold API reflects a type
as a structural interface.
As the basis of the GraphQL manifold API, interfaces hide implementation detail that may otherwise complicate
the evolution of the API e.g., as new features are added to the GraphQL specification.
An input
is basically a type
intended for use as a mutation constraint. It is identical to type
in terms of
representation in the manifold API.
interface
abstractions are structural interfaces in the manifold API. They are structured just like type
abstractions, but do not have creation methods.
The enum
abstraction maps directly to a Java enum in the manifold API.
Since the JVM does not provide a union type the manifold API approximates it as an interface extending the least
upper bound (LUB) interface
abstraction of the union component types, or extends nothing if no LUB interface
exists.
Its declared properties consist of the intersection of all the properties of the union component types. As such a
property outside the intersection must be accessed by casting a union to a union component type declaring the
property. Note a GraphQL query can provide a discriminator in terms of the __typename
property to facilitate
conditional access to union properties.
The manifold API fully supports GraphQL scalars and also provides a host of non-standard but commonly used types. See Scalar Types below.
Not to be confused with Manifold Fragments, a GraphQL fragment is generally a query you can directly reference inside other queries so you don't have to copy and paste the same set of fields. Instead you simply reference the name of the fragment. This not only helps reduce the size of queries, but also prevents copy/paste errors and makes your queries more readable.
Similar to the type
abstraction, the manifold API exposes a query
as a structural interface. Non-null query
parameters translate to parameters in the create
and builder
methods, and the nullable parameters are getter/setter
methods and with methods in the builder. Additionally the structural interfaces allow the query implementation to be free
of POJOs, marshalling, and other mapping code present in conventional API tooling. As such a query structural interface
directly overlays a raw GraphQL query response; there is absolutely zero processing of query results after a query
HTTP request. The only processing involved happens when a scalar value must be coerced to a type-safe value; this
happens lazily on a per call-site basis.
The manifold API treatment of mutations is identical to queries. See query
above.
not implemented
You can add properties, interfaces, and annotations to existing types using the extend
construct. The manifold API
fully supports all type extensions.
GraphQL specifies several standard scalar types, in addition to these Manifold provides several other non-standard, but commonly used types. These include:
Name | Persists As | Java Type |
---|---|---|
Byte | byte | byte or java.lang.Byte if nullable |
Char | char | char or java.lang.Character if nullable |
Character | char | char or java.lang.Character if nullable |
Int | integer | int or java.lang.Integer if nullable |
Integer | integer | int or java.lang.Integer if nullable |
Long | long | long or java.lang.Long if nullable |
Float | double | double or java.lang.Double if nullable |
Double | double | double or java.lang.Double if nullable |
Boolean | boolean | boolean or java.lang.Boolean if nullable |
String | string | java.lang.String |
ID | string | java.lang.String |
Date | string | java.time.LocalDate |
LocalDate | string | java.time.LocalDate |
Time | string | java.time.LocalTime |
LocalTime | string | java.time.LocalTime |
DateTime | string | java.time.LocalDateTime |
LocalDateTime | string | java.time.LocalDateTime |
Instant | integer | java.time.Instant |
BigInteger | string | java.math.BigInteger |
BigDecimal | string | java.math.BigDecimal |
Binary | string | manifold.json.rt.api.OctetEncoding |
Octet | string | manifold.json.rt.api.OctetEncoding |
Base64 | string | manifold.json.rt.api.Base64Encoding |
Additionally, Manifold includes an API you can implement to provide your own custom scalar types. Implement the
manifold.json.rt.api.IJsonFormatTypeCoercer
interface as a
service provider.
Note, the scalar's persisted JSON type and format must match the GraphQL server's type and format. The GraphQL server's published schema should include this information. For instance, a date or time value typically persists as a String type and adheres to a specific date/time format.
If you've implemented format type resolvers for JSON Schema using
manifold-json
, you can share them with your GraphQL APIs; they already implementIJsonFormatTypeCoercer
.
To use GraphQL files in your project, at least one of them must define a schema. This can be done explicitly with a
schema
declaration:
schema {
query: MyQueryRoot
mutation: MyMutationRoot
}
type MyQueryRoot {
. . .
}
type MyMutationRoot {
. . .
}
Or, it can be done implicitly with a naming convention where the operation roots are aptly named "Query" and "Mutation":
type Query {
. . .
}
type Mutation {
. . .
}
Either way, if your project only defines one schema, Manifold assumes all GraphQL files are scoped to that schema.
For a project with multiple schemas, one or more .graphqlconfig
files define the scope for each schema. The scope provides
information such as a unique name and file paths to establish the scope's boundaries. Without this information
it is otherwise difficult to know which schema a query or mutation refers to, particularly if any of the field names
overlap.
A .graphqlconfig
file follows the graphql-config
format. For Manifold, it is recommended to use multiple .graphqlconfig files as opposed to a single multi-project one.
For instance, it is best to organize GraphQL resource files into separate namespaces like this:
- resources/
. . .
- schema_one/
- .graphqlconfig
- graphql files
- schema_two/
- .graphqlconfig
- graphql files
This way your GraphQL types are nicely separated and your .graphqlconfig files easy to define:
{
"name": "scope_one",
"schemaPath": "./schema.graphql"
}
This layout takes advantage of the relative location of the schema files. For instance, GraphQL resource
files in the same directory or subdirectory of the schema file named in schemaPath
are automatically in scope.
You can also specify multiple schema file locations using "schema"
instead of "schemaPath"
:
{
"name": "scope_one",
"schema": [
"./foo/schema_part1.graphql",
"./bar/schema_part2.graphql"
]
}
The complete graphql-config format is found here. Manifold supports both the newer and legacy formats.
If you are using the JS-GraphQL IntelliJ plugin, you may have noticed it also uses .graphqlconfig files. You can use the same files with Manifold as well.
Note, this is a completely separate feature from GraphQL fragments and does not involve the
fragment
keyword
You can now inline resource content such as GraphQL directly in Java source as a type-safe resource fragment. This means you can inline a type-safe GraphQL query exactly where you use it in your Java code -- no need to create a separate resource file.
A fragment can be either a declaration or an expression. A fragment declaration is inlined as a multi-line comment like this:
/*[MyQuery.graphql/]
query Movies($title: String, $genre: Genre, $releaseDate: Date) {
movies(title: $title, genre: $genre, releaseDate: $releaseDate) {
id
title
genre
releaseDate
}
}
*/
var query = MyQuery.Movies.builder().withGenre(Action).build();
out.println(query.toString());
For a project configured for multiple schemas via .graphqlconfig
files, the
declaration must also specify the scope of the schema. For the example above, if the schema for the movies
query were
in a scope named "my_movies", the declaration encodes this name like so:
[MyQuery.graphql:my_movies/]
A fragment expression is inlined using a String literal:
var query = "[.graphql/] query MovieQuery($genre: Genre){ movies(genre: $genre){ genre } }";
var result = query.builder().build().request("").post();
result.getMovies().forEach( e -> e.getGenre() );
For a project configured for multiple schemas via .graphqlconfig
files, the
expression must also specify the scope of the schema. For the example above, if the schema for the movies
query were
in a scope named "my_movies", the expression encodes this name like so:
[.graphql:my_movies/]
With Java text block String literals you can author multi-line fragment expressions like this:
IntelliJ and Android Studio users...
Get the JS GraphQL plugin for rich editing of inlined GraphQL fragments, it pairs exceptionally well with the Manifold plugin.
Read more about fragments in the core Manifold docs.
Manifold is fully supported in IntelliJ IDEA and Android Studio.
Get the Manifold plugin directly from within the IDE via:
Settings ➜ Plugins ➜ Marketplace ➜ search: Manifold
Experiment with the Manifold GraphQL Sample Project via:
File ➜ New ➜ Project from Version Control ➜ Git
Enter: *https://github.com/manifold-systems/manifold-sample-graphql-app.git
Use the plugin to boost your productivity. Use code completion to conveniently build queries and discover the schema's fluent API. Navigate to/from call-sites and GraphQL schema file elements. Make changes to your query schema files and use the changes immediately, no compilation! Find usages of any element in your schema files. Perform rename refactors to quickly and safely make project-wide changes.
Note: Don't forget to install the JS GraphQL plugin for superb GraphQL file editing support in your project. It pairs well with the Manifold plugin.
The manifold-graphql
project is defined with Maven. To build it install Maven and a Java 8 JDK and run the following
command.
mvn compile
The manifold-graphql
dependency works with all build tooling, including Maven and Gradle. It fully supports Java
versions 8 - 21.
This project consists of two modules:
manifold-graphql
manifold-graphql-rt
For optimal performance and to work with Android and other JVM languages it is recommended to:
- Add a dependency on
manifold-graphql-rt
(Gradle: "implementation", Maven: "compile") - Add
manifold-graphql
to the annotationProcessor path (Gradle: "annotationProcessor", Maven: "annotationProcessorPaths")
If you are not using Maven or Gradle, you can download the latest binaries here.
Note, if you are targeting Android, please see the Android docs.
Note, if you are using Kotlin, please see the Kotlin docs.
Here is a sample build.gradle
script. Change targetCompatibility
and sourceCompatibility
to your desired Java
version (8 - 21), the script takes care of the rest.
plugins {
id 'java'
}
group 'systems.manifold'
version '1.0-SNAPSHOT'
targetCompatibility = 11
sourceCompatibility = 11
repositories {
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
implementation 'systems.manifold:manifold-graphql-rt:2025.1.1'
testImplementation 'junit:junit:4.12'
// Add manifold to -processorpath for javac
annotationProcessor 'systems.manifold:manifold-graphql:2025.1.1'
testAnnotationProcessor 'systems.manifold:manifold-graphql:2025.1.1'
}
if (JavaVersion.current() != JavaVersion.VERSION_1_8 &&
sourceSets.main.allJava.files.any {it.name == "module-info.java"}) {
tasks.withType(JavaCompile) {
// if you DO define a module-info.java file:
options.compilerArgs += ['-Xplugin:Manifold', '--module-path', it.classpath.asPath]
}
} else {
tasks.withType(JavaCompile) {
// If you DO NOT define a module-info.java file:
options.compilerArgs += ['-Xplugin:Manifold']
}
}
Use with accompanying settings.gradle
file:
rootProject.name = 'MyProject'
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-graphql-app</artifactId>
<version>0.1-SNAPSHOT</version>
<name>My GraphQL App</name>
<properties>
<!-- set latest manifold version here -->
<manifold.version>2025.1.1</manifold.version>
</properties>
<dependencies>
<dependency>
<groupId>systems.manifold</groupId>
<artifactId>manifold-graphql-rt</artifactId>
<version>${manifold.version}</version>
</dependency>
</dependencies>
<!--Add the -Xplugin:Manifold argument for the javac compiler-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<!-- Configure manifold plugin-->
<arg>-Xplugin:Manifold</arg>
</compilerArgs>
<!-- Add the processor path for the plugin -->
<annotationProcessorPaths>
<path>
<groupId>systems.manifold</groupId>
<artifactId>manifold-graphql</artifactId>
<version>${manifold.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Open source Manifold is free and licensed under the Apache 2.0 license.
For the versions available, see the tags on this repository.