The way you’ve been developing so far is known as schema-first, as you always start by defining the schema. This style has important benefits, discussed at the beginning of this tutorial, and it works well for new projects, where no legacy code exists. Still, you may have noticed that in strongly and statically typed languages, like Java, it leads to a lot of duplication. For example, revisit the way you developed the Link
type.
You defined it in the schema:
type Link {
id: ID!
url: String!
description: String
}
and then you created a corresponding POJO:
public class Link {
private final String id;
private final String url;
private final String description;
//constructors, getters and setters
//...
}
Both of these blocks contain the exact same information. Worse yet, changing one requires immediate change to the other. This makes refactoring risky and cumbersome. On the other hand, if you’re trying to introduce a GraphQL API into an existing project, writing the schema practically means re-describing the entire existing model. This is both expensive and error-prone, and still suffers from duplication.
A common alternative to the schema-first style, known as code-first, is generating the schema from the existing model. This keeps the schema and the model in sync, easing refactoring. It also works well in projects where GraphQL is introduced on top of an existing codebase. The downside of this approach is that the schema doesn’t exist until some server code is written, introducing a dependency between the client-side and server-side work. One workaround would be using stubs on the server to generate the schema quickly, then developing the real server code in parallel with the client.
The Java/GraphQL ecosystem spawned a few libraries that facilitate this style of development. You can find them listed here. An example using graphql-spqr
, written by yours truly, follows below.
Additionally, it will be much more comfortable to work if the method parameter names are preserved (you’ll understand why in a second).
Make sure you rebuild the project now (e.g. run mvn clean package
) for the new option to take effect. Then, restart Jetty.
In order to generate a schema similar to the one you’ve been working on so far, but this time using the code-first style you’d (unsurprisingly) start from the business logic. It is fortunate that you already have some business logic ready, in Query
, Mutation
and *Resolver
classes, as it simulates introducing GraphQL into an existing project.
The easiest way to demonstrate graphql-spqr
is by using annotations, but note that they’re entirely optional.
A few things to note about this code:
GraphQLRootResolver
is no longer needed (nor is the dependency to graphql-java-tools
). In fact, graphql-spqr
goes to great lengths to ensure the code needs no special classes, interfaces or any modifications in order to be exposed over GraphQL-parameters
javac option enabled when compiling). Using @GraphQLArgument
is a way to change the name and set the default value. All of this is doable without annotations as well.The point of interest in this block:
implements GraphQLResolver<Link>
@GraphQLContext
is used to wire external methods into types. This mapping is semantically the same as if the Link
class contained a method public User postedBy() {...}
. In this manner, it is possible to keep the logic separate from data, yet still produce deeply nested structures.Things to note:
@GraphQLMutation
AuthContext
directly via @GraphQLRootContext
. No more need for DataFetchingEnvironment
. This nicely removes the dependency to graphql-java
specific code in the logic layer.If you now fire up GraphiQL, you’d get the exact same result as before:
The important points to note:
Link
s into the top level queries (allLinks
inside Query
), embedded ones (postedBy
inside LinkResolver
) and mutations (createLink
inside Mutation
). All the queries and mutations operating on links could have been placed into a single class (e.g. LinkService
), yet having them separate was not a hurdle either. This implies that your legacy code and best practices can stay untouched. This is just a glance at the alternative style of development. There are many more possibilities to explore, so take a look at what the ecosystem has to offer. For more info on graphql-spqr
check out the project page, and for a full example see here.