GraphQL in Java
Marcin Stachniuk
stachniuk.com
/mstachniuk/graphql-java-example
@MarcinStachniuk
wroclaw.jug.pl
shipkit.org
Our technology stack
ReactJS
Relay GraphQL
TypeScript
@MarcinStachniukGraphQL in Java
GraphQL Java implementations
How to implement GraphQL in Java?
@MarcinStachniuk
/graphql-java/graphql-java
Code First
GraphQL in Java
GraphQL Simple API
GET /customers/2?fields=id,name,email,orders(id,status)
type Customer {
id: ID!
name: String!
email: String!
company: Company
orders: [Order]
}
type Order {
id: ID!
status: Status
}
enum Status {
NEW, CANCELED, DONE
}
{
"data": {
"customer": {
"id": "2",
"name": "name",
"orders": [
{
"id": "55",
"status": "NEW"
},
{
"id": "66",
"status": "DONE"
}
] } } }
{
customer(id: "2") {
id
name
orders {
id
status
}
}
}
RE
ST
@MarcinStachniukGraphQL in Java
Code First approach
private GraphQLFieldDefinition customerDefinition() {
return GraphQLFieldDefinition.newFieldDefinition()
.name("customer")
.argument(GraphQLArgument.newArgument()
.name("id")
.type(new GraphQLNonNull(GraphQLString)))
.type(new GraphQLNonNull(GraphQLObjectType.newObject()
.name("Customer")
.field(GraphQLFieldDefinition.newFieldDefinition()
.name("id")
.description("fields with ! are not null")
.type(new GraphQLNonNull(GraphQLID))
.build())
….
.build()))
.dataFetcher(customerFetcher)
.build();
}
@MarcinStachniukGraphQL in Java
How to implement DataFetcher for queries
GET /customers/2?fields=id,name,email,orders(id,status)
@Component
public class CustomerFetcher extends PropertyDataFetcher<Customer> {
@Autowired
private CustomerService customerService;
@Override
public Customer get(DataFetchingEnvironment environment) {
String id = environment.getArgument("id");
return customerService.getCustomerById(id);
}
}
RE
ST
{
customer(id: "2") {
id
name
orders {
id
status
}
}
}
@MarcinStachniukGraphQL in Java
How to implement DataFetcher for queries
GET /customers/2?fields=id,name,email,orders(id,status)
public class Customer {
private String id;
private String name;
private String email; // getters are not required
}
RE
ST
{
customer(id: "2") {
id
name
orders {
id
status
}
}
}
@MarcinStachniuk
public class OrderDataFetcher extends PropertyDataFetcher<List<Order>> {
@Override
public List<Order> get(DataFetchingEnvironment environment) {
Customer source = environment.getSource();
String customerId = source.getId();
return orderService.getOrdersByCustomerId(customerId);
}
}
GraphQL in Java
GraphQL mutations
input CreateCustomerInput {
name: String
email: String
clientMutationId: String!
}
type CreateCustomerPayload {
customer: Customer
clientMutationId: String!
}
type Mutation {
createCustomer(input: CreateCustomerInput):
CreateCustomerPayload!
}
{
"data": {
"createCustomer": {
"customer": {
"id": "40",
},
"clientMutationId":
"123"
}
}
}
POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123
mutation {
createCustomer(input: {
name: "MyName"
email: "me@me.com"
clientMutationId: "123"
}) {
customer {
id
}
clientMutationId
}
}
RE
ST
@MarcinStachniukGraphQL in Java
How to implement DataFetcher for mutations
POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123
@Component
public class CreateCustomersFetcher extends
PropertyDataFetcher<CreateCustomersPayload> {
@Override
public CreateCustomerPayload get(DataFetchingEnvironment env) {
Map<String, Object> input = env.getArgument("input");
String name = (String) input.get("name");
String email = (String) input.get("email");
String clientMutationId = (String) input.get("clientMutationId");
Customer customer = customerService.create(name, email);
return new CreateCustomerPayload(customer, clientMutationId);
}
RE
ST
mutation {
createCustomer(input: {
name: "MyName"
email: "me@me.com"
clientMutationId: "123"
}) {
customer {
id
}
clientMutationId
}
}
@MarcinStachniukGraphQL in Java
GraphQL Interface
GET /users?fields=id,name,superAdmin,permissions
type Query {
users: [User]
}
interface User {
id: ID!
name: String!
email: String!
}
type Admin implements User {
superAdmin: Boolean! // + id...
}
type Moderator implements User {
permissions: [String] // + id...
}
{
"data": {
"users": [
{
"id": "777",
"name": "Admin",
"superAdmin": true
},
{
"id": "888",
"name": "Moderator",
"permissions": [
"Delete Customer",
"Delete comment"
]}]}}
query getUsers {
users {
... on Admin {
id
name
superAdmin
}
... on Moderator {
id
name
permissions
}
}
}
RE
ST
@MarcinStachniukGraphQL in Java
In case Interfaces and Unions - Type Resolver is needed
public class UserTypeResolver implements TypeResolver {
@Override
public GraphQLObjectType getType(TypeResolutionEnvironment env) {
Object javaObject = env.getObject();
if (javaObject instanceof Admin) {
return env.getSchema().getObjectType("Admin");
} else if (javaObject instanceof Moderator) {
return env.getSchema().getObjectType("Moderator");
} else {
throw new RuntimeException("Unknown type " + javaObject.getClass().getName());
}
}
}
public interface User {...}
public class Admin implements User {...}
public class Moderator implements User {...}
@MarcinStachniukGraphQL in Java
Glue everything together
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder ->
builder.dataFetcher("customer", customerFetcher))
.type("Mutation", builder ->
builder.dataFetcher("createCustomer", createCustomerFetcher))
.type(newTypeWiring("User")
.typeResolver(new UserTypeResolver())
.build())
...
.build();
@MarcinStachniukGraphQL in Java
Code First approach - How to build
Introspection
query
Introspection
response
Replace Relay
definitions
@MarcinStachniuk
Typescript relay
plugin
GraphQL in Java
GraphQL Java implementations
How to implement GraphQL in Java?
@MarcinStachniuk
/graphql-java/graphql-java
Schema First
GraphQL in Java
Schema First approach
type Customer {
# fields with ! are required
id: ID!
name: String!
email: String!
company: Company
orders: [Order]
}
*.graphqls
SchemaParser schemaParser = new SchemaParser();
File file = // ...
TypeDefinitionRegistry registry = schemaParser.parse(file);
SchemaGenerator schemaGenerator = new SchemaGenerator();
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder ->
builder.dataFetcher("customer", customerFetcher))
// ...
.build();
return schemaGenerator.makeExecutableSchema(registry, runtimeWiring);
@MarcinStachniukGraphQL in Java
Schema First approach in comparison to Code First
private GraphQLFieldDefinition customerDefinition() {
return GraphQLFieldDefinition.newFieldDefinition()
.name("customer")
.argument(GraphQLArgument.newArgument()
.name("id")
.type(new GraphQLNonNull(GraphQLString)))
.type(new GraphQLNonNull(GraphQLObjectType.newObject()
.name("Customer")
.field(GraphQLFieldDefinition.newFieldDefinition()
.name("id")
.description("fields with ! are not null")
.type(new GraphQLNonNull(GraphQLID))
.build())
….
.build()))
.dataFetcher(customerFetcher)
.build();
}
Schema First approach
type Query {
customer(id: String!): Customer!
}
type Customer {
#fields with ! are not null
id: ID!
name: String!
email: String!
company: Company
orders: [Order]
}
@MarcinStachniukGraphQL in Java
Schema First approach - How to build
model.graphqls
@MarcinStachniukGraphQL in Java
GraphQL SPQR
@MarcinStachniuk
/leangen/graphql-spqr
Senātus Populusque Rōmānus
The Roman Senate and People
pronounced like speaker
GraphQL in Java
SPQR approach
public class UserService {
@GraphQLQuery(name = "user")
public User getById(@GraphQLArgument(name = "id") Integer id) {
//...
}
}
@MarcinStachniuk
type Query {
user(id: Int): User
}
GraphQL in Java
By default argument names will be arg0, arg1,...
You can use @GraphQLArgument or
Compile code with -parameters option
SPQR approach
public class User {
//...
@GraphQLQuery(name = "name", description = "A person's name")
public String getName() {
return name;
}
}
@MarcinStachniuk
type User {
# A person's name
name: String
}
GraphQL in Java
SPQR approach - Smart conventions
@GraphQLType(name = "Item")
public class SpqrItem {
private String id;
private int amount;
// ...
@GraphQLId
@GraphQLNonNull
@GraphQLQuery(name = "id", description = "Item id")
public String getId() {
return id;
}
public int getAmount() {
return amount;
}
}
@MarcinStachniuk
// 'ID' type in schema, add 'implements Node'
// add ! (non null) in schema
// optional
// add ! (non null) because it’s int
GraphQL in Java
Classic Code First Approach
Pros:
● Stable library
● Some Javadoc and
documentation
● It was the only way at the
beginning to define a schema
@MarcinStachniuk
Cons:
● Hard to maintain
● Missing big picture
● Backend part needs to be
implemented first
● One DataFetcher for one
operation
● No possibility to mix with
Schema First
● No easy way to migrate to
Schema First or SPQR
GraphQL in Java
Schema First Approach
Pros:
● Easy to maintain and understand
● Stable library
● Some Javadoc and
documentation
● DSL defined in specification
● Reduce boilerplate to write
● A possibility to split schema into
more files
● Parallel development (frontend &
backend)
@MarcinStachniuk
Cons:
● Possible mismatch between
types in schema file and Java
code
● Binding between operations
defined in schema and
DataFetcher's is needed
● One DataFetcher for one
operation
GraphQL in Java
SPQR Approach
Pros:
● The author reply to Github issues
quickly
● Reduce boilerplate to write
● No mismatch between Schema
and Java code
● No binding between schema and
DataFetchers is needed
● Many “DataFetchers” in single
class
● Smart Relay conventions
@MarcinStachniuk
Cons:
● Production ready?
● Zero Javadoc or other
documentation
● Missing big picture
● Backend part needs to be
implemented first
● Ugly names for generics:
MutationPayload<User> =>
MutationPayload_User
● Hard to change smart
conventions
GraphQL in Java
What to choose?
Classic Code First Approach is the worst
@MarcinStachniuk
● Big project
● High risk project
● Old project
● Small project
● Low risk project
● New Project
?
GraphQL in Java
Prisma version?
@MarcinStachniukGraphQL in Java
https://www.prisma.io/blog/the-problems-of-schema-first-graphql-developm
ent-x1mn4cb0tyl3
Alternatives out of scope this presentation
@MarcinStachniukGraphQL in Java
● GraphQL Java Tools
https://github.com/graphql-java-kickstart/graphql-java-tools
● graphql-spring-boot-starter
https://github.com/graphql-java-kickstart/graphql-spring-boot
● Micronaut GraphQL
https://github.com/micronaut-projects/micronaut-graphql
● graphql-kotlin https://github.com/ExpediaGroup/graphql-kotlin
● KGraphQL https://github.com/aPureBase/KGraphQL
● Sangria https://sangria-graphql.org/
● Lacinia https://github.com/walmartlabs/lacinia
● Gorm GraphQL https://github.com/grails/gorm-graphql/
● GQL https://grooviter.github.io/gql/
GraphQL Testing
@MarcinStachniukGraphQL in Java
Testing GraphQL
@SpringBootTest
@ContextConfiguration(classes = Main)
class CustomerFetcherSpec extends Specification {
@Autowired
GraphQLSchema graphQLSchema
GraphQL graphQL
def setup() {
graphQL = GraphQL.newGraphQL(graphQLSchema).build()
}
@MarcinStachniukGraphQL in Java
Testing GraphQL
def "should get customer by id"() {
given:
def query = """{ customer(id: "2") { … } }"""
def expected = [ "customer": [ … ] ]
when:
def result = graphQL.execute(query)
then:
result.data == expected
}
@MarcinStachniukGraphQL in Java
Versioning
type Query {
customer(id: String!): Customer! @deprecated(reason: "not used ...")
@MarcinStachniukGraphQL in Java
Extras
@MarcinStachniuk
Extras
(challenges, tools, good practices)
GraphQL in Java
From Code First to Schema First migration
https://github.com/mstachniuk/graphql-schema-from-introspection-generator
@MarcinStachniukGraphQL in Java
From Code First to Schema First migration alternatives
● In graphql-java - see: IntrospectionResultToSchema
● In graphql-js:
const graphql = require("graphql");
const schema = require("path/to/schema.json");
const clientSchema = graphql.buildClientSchema(schema.data);
const schemaString = graphql.printSchema(clientSchema);
console.log(schemaString)
@MarcinStachniukGraphQL in Java
GraphQL downsides: N+1 problem
{
customers { 1 call
id
name
orders { n calls
id
status
}
}
}
java-dataloader
● Add async BatchLoader
● Add caching
@MarcinStachniuk
graphQL = GraphQL.newGraphQL(schema)
.queryExecutionStrategy(
new BatchedExecutionStrategy())
.build();
+ @Batched in DataFetcher#get()
GraphQL in Java
GraphQL downsides: N+1 problem
If you have N + 1 problem
use java-dataloader
@MarcinStachniukGraphQL in Java
Complex Queries
fragment Friends on Friend {
id
name
friends {
id
name
friends {
id
name
...
}
}
}
@MarcinStachniuk
graphQL = GraphQL.newGraphQL(schema)
.instrumentation(
new ChainedInstrumentation(asList(
new MaxQueryComplexityInstrumentation(20),
new MaxQueryDepthInstrumentation(2)
)))
.build();
GraphQL in Java
Complex Queries
Performance killer?
Define instrumentation!
@MarcinStachniukGraphQL in Java
Recommended tools
@MarcinStachniukGraphQL in Java
Recommended tools: GraphiQL
@MarcinStachniukGraphQL in Java
Recommended tools: Altair GraphQL Client
@MarcinStachniukGraphQL in Java
https://altair.sirmuel.design/
Recommended tools: Relay
@MarcinStachniukGraphQL in Java @MarcinStachniuk
user(...) {
photo(width: "120", height: "120")
}
user(...) {
name
}
user(...) {
email
}
user(...) {
name
email
photo(width: "120", height: "120")
}
GraphQL in Java
Recommended tools: GraphQL schema linter
https://github.com/cjoudrey/graphql-schema-linter
Validate GraphQL schema definitions against a set of rules
@MarcinStachniukGraphQL in Java
Recommended tools: Graphql network - Chrome extension
@MarcinStachniuk
https://chrome.google.com/webstore/detail/graphql-network/ool
bobjfnfdnpgjcaphejlodpbefhmbn
GraphQL in Java
Recommended tools: Graphql network - Chrome extension
@MarcinStachniukGraphQL in Java
More libraries and projects related to graphql-java
https://github.com/graphql-java/awesome-graphql-java
@MarcinStachniukGraphQL in Java
Good practices
● Always generate GraphQL Schema and commit into your repository
● Follow conventions (e.g. Relay)
● Validate your schema
● Do not use classic code first approach to define schema
@MarcinStachniukGraphQL in Java
Q&A
@MarcinStachniukGraphQL in Java
GraphQL in Java
hank
you!

Wroclaw GraphQL - GraphQL in Java

  • 1.
  • 2.
  • 3.
    Our technology stack ReactJS RelayGraphQL TypeScript @MarcinStachniukGraphQL in Java
  • 4.
    GraphQL Java implementations Howto implement GraphQL in Java? @MarcinStachniuk /graphql-java/graphql-java Code First GraphQL in Java
  • 5.
    GraphQL Simple API GET/customers/2?fields=id,name,email,orders(id,status) type Customer { id: ID! name: String! email: String! company: Company orders: [Order] } type Order { id: ID! status: Status } enum Status { NEW, CANCELED, DONE } { "data": { "customer": { "id": "2", "name": "name", "orders": [ { "id": "55", "status": "NEW" }, { "id": "66", "status": "DONE" } ] } } } { customer(id: "2") { id name orders { id status } } } RE ST @MarcinStachniukGraphQL in Java
  • 6.
    Code First approach privateGraphQLFieldDefinition customerDefinition() { return GraphQLFieldDefinition.newFieldDefinition() .name("customer") .argument(GraphQLArgument.newArgument() .name("id") .type(new GraphQLNonNull(GraphQLString))) .type(new GraphQLNonNull(GraphQLObjectType.newObject() .name("Customer") .field(GraphQLFieldDefinition.newFieldDefinition() .name("id") .description("fields with ! are not null") .type(new GraphQLNonNull(GraphQLID)) .build()) …. .build())) .dataFetcher(customerFetcher) .build(); } @MarcinStachniukGraphQL in Java
  • 7.
    How to implementDataFetcher for queries GET /customers/2?fields=id,name,email,orders(id,status) @Component public class CustomerFetcher extends PropertyDataFetcher<Customer> { @Autowired private CustomerService customerService; @Override public Customer get(DataFetchingEnvironment environment) { String id = environment.getArgument("id"); return customerService.getCustomerById(id); } } RE ST { customer(id: "2") { id name orders { id status } } } @MarcinStachniukGraphQL in Java
  • 8.
    How to implementDataFetcher for queries GET /customers/2?fields=id,name,email,orders(id,status) public class Customer { private String id; private String name; private String email; // getters are not required } RE ST { customer(id: "2") { id name orders { id status } } } @MarcinStachniuk public class OrderDataFetcher extends PropertyDataFetcher<List<Order>> { @Override public List<Order> get(DataFetchingEnvironment environment) { Customer source = environment.getSource(); String customerId = source.getId(); return orderService.getOrdersByCustomerId(customerId); } } GraphQL in Java
  • 9.
    GraphQL mutations input CreateCustomerInput{ name: String email: String clientMutationId: String! } type CreateCustomerPayload { customer: Customer clientMutationId: String! } type Mutation { createCustomer(input: CreateCustomerInput): CreateCustomerPayload! } { "data": { "createCustomer": { "customer": { "id": "40", }, "clientMutationId": "123" } } } POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123 mutation { createCustomer(input: { name: "MyName" email: "me@me.com" clientMutationId: "123" }) { customer { id } clientMutationId } } RE ST @MarcinStachniukGraphQL in Java
  • 10.
    How to implementDataFetcher for mutations POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123 @Component public class CreateCustomersFetcher extends PropertyDataFetcher<CreateCustomersPayload> { @Override public CreateCustomerPayload get(DataFetchingEnvironment env) { Map<String, Object> input = env.getArgument("input"); String name = (String) input.get("name"); String email = (String) input.get("email"); String clientMutationId = (String) input.get("clientMutationId"); Customer customer = customerService.create(name, email); return new CreateCustomerPayload(customer, clientMutationId); } RE ST mutation { createCustomer(input: { name: "MyName" email: "me@me.com" clientMutationId: "123" }) { customer { id } clientMutationId } } @MarcinStachniukGraphQL in Java
  • 11.
    GraphQL Interface GET /users?fields=id,name,superAdmin,permissions typeQuery { users: [User] } interface User { id: ID! name: String! email: String! } type Admin implements User { superAdmin: Boolean! // + id... } type Moderator implements User { permissions: [String] // + id... } { "data": { "users": [ { "id": "777", "name": "Admin", "superAdmin": true }, { "id": "888", "name": "Moderator", "permissions": [ "Delete Customer", "Delete comment" ]}]}} query getUsers { users { ... on Admin { id name superAdmin } ... on Moderator { id name permissions } } } RE ST @MarcinStachniukGraphQL in Java
  • 12.
    In case Interfacesand Unions - Type Resolver is needed public class UserTypeResolver implements TypeResolver { @Override public GraphQLObjectType getType(TypeResolutionEnvironment env) { Object javaObject = env.getObject(); if (javaObject instanceof Admin) { return env.getSchema().getObjectType("Admin"); } else if (javaObject instanceof Moderator) { return env.getSchema().getObjectType("Moderator"); } else { throw new RuntimeException("Unknown type " + javaObject.getClass().getName()); } } } public interface User {...} public class Admin implements User {...} public class Moderator implements User {...} @MarcinStachniukGraphQL in Java
  • 13.
    Glue everything together RuntimeWiringruntimeWiring = RuntimeWiring.newRuntimeWiring() .type("Query", builder -> builder.dataFetcher("customer", customerFetcher)) .type("Mutation", builder -> builder.dataFetcher("createCustomer", createCustomerFetcher)) .type(newTypeWiring("User") .typeResolver(new UserTypeResolver()) .build()) ... .build(); @MarcinStachniukGraphQL in Java
  • 14.
    Code First approach- How to build Introspection query Introspection response Replace Relay definitions @MarcinStachniuk Typescript relay plugin GraphQL in Java
  • 15.
    GraphQL Java implementations Howto implement GraphQL in Java? @MarcinStachniuk /graphql-java/graphql-java Schema First GraphQL in Java
  • 16.
    Schema First approach typeCustomer { # fields with ! are required id: ID! name: String! email: String! company: Company orders: [Order] } *.graphqls SchemaParser schemaParser = new SchemaParser(); File file = // ... TypeDefinitionRegistry registry = schemaParser.parse(file); SchemaGenerator schemaGenerator = new SchemaGenerator(); RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring() .type("Query", builder -> builder.dataFetcher("customer", customerFetcher)) // ... .build(); return schemaGenerator.makeExecutableSchema(registry, runtimeWiring); @MarcinStachniukGraphQL in Java
  • 17.
    Schema First approachin comparison to Code First private GraphQLFieldDefinition customerDefinition() { return GraphQLFieldDefinition.newFieldDefinition() .name("customer") .argument(GraphQLArgument.newArgument() .name("id") .type(new GraphQLNonNull(GraphQLString))) .type(new GraphQLNonNull(GraphQLObjectType.newObject() .name("Customer") .field(GraphQLFieldDefinition.newFieldDefinition() .name("id") .description("fields with ! are not null") .type(new GraphQLNonNull(GraphQLID)) .build()) …. .build())) .dataFetcher(customerFetcher) .build(); } Schema First approach type Query { customer(id: String!): Customer! } type Customer { #fields with ! are not null id: ID! name: String! email: String! company: Company orders: [Order] } @MarcinStachniukGraphQL in Java
  • 18.
    Schema First approach- How to build model.graphqls @MarcinStachniukGraphQL in Java
  • 19.
    GraphQL SPQR @MarcinStachniuk /leangen/graphql-spqr Senātus PopulusqueRōmānus The Roman Senate and People pronounced like speaker GraphQL in Java
  • 20.
    SPQR approach public classUserService { @GraphQLQuery(name = "user") public User getById(@GraphQLArgument(name = "id") Integer id) { //... } } @MarcinStachniuk type Query { user(id: Int): User } GraphQL in Java By default argument names will be arg0, arg1,... You can use @GraphQLArgument or Compile code with -parameters option
  • 21.
    SPQR approach public classUser { //... @GraphQLQuery(name = "name", description = "A person's name") public String getName() { return name; } } @MarcinStachniuk type User { # A person's name name: String } GraphQL in Java
  • 22.
    SPQR approach -Smart conventions @GraphQLType(name = "Item") public class SpqrItem { private String id; private int amount; // ... @GraphQLId @GraphQLNonNull @GraphQLQuery(name = "id", description = "Item id") public String getId() { return id; } public int getAmount() { return amount; } } @MarcinStachniuk // 'ID' type in schema, add 'implements Node' // add ! (non null) in schema // optional // add ! (non null) because it’s int GraphQL in Java
  • 23.
    Classic Code FirstApproach Pros: ● Stable library ● Some Javadoc and documentation ● It was the only way at the beginning to define a schema @MarcinStachniuk Cons: ● Hard to maintain ● Missing big picture ● Backend part needs to be implemented first ● One DataFetcher for one operation ● No possibility to mix with Schema First ● No easy way to migrate to Schema First or SPQR GraphQL in Java
  • 24.
    Schema First Approach Pros: ●Easy to maintain and understand ● Stable library ● Some Javadoc and documentation ● DSL defined in specification ● Reduce boilerplate to write ● A possibility to split schema into more files ● Parallel development (frontend & backend) @MarcinStachniuk Cons: ● Possible mismatch between types in schema file and Java code ● Binding between operations defined in schema and DataFetcher's is needed ● One DataFetcher for one operation GraphQL in Java
  • 25.
    SPQR Approach Pros: ● Theauthor reply to Github issues quickly ● Reduce boilerplate to write ● No mismatch between Schema and Java code ● No binding between schema and DataFetchers is needed ● Many “DataFetchers” in single class ● Smart Relay conventions @MarcinStachniuk Cons: ● Production ready? ● Zero Javadoc or other documentation ● Missing big picture ● Backend part needs to be implemented first ● Ugly names for generics: MutationPayload<User> => MutationPayload_User ● Hard to change smart conventions GraphQL in Java
  • 26.
    What to choose? ClassicCode First Approach is the worst @MarcinStachniuk ● Big project ● High risk project ● Old project ● Small project ● Low risk project ● New Project ? GraphQL in Java
  • 27.
    Prisma version? @MarcinStachniukGraphQL inJava https://www.prisma.io/blog/the-problems-of-schema-first-graphql-developm ent-x1mn4cb0tyl3
  • 28.
    Alternatives out ofscope this presentation @MarcinStachniukGraphQL in Java ● GraphQL Java Tools https://github.com/graphql-java-kickstart/graphql-java-tools ● graphql-spring-boot-starter https://github.com/graphql-java-kickstart/graphql-spring-boot ● Micronaut GraphQL https://github.com/micronaut-projects/micronaut-graphql ● graphql-kotlin https://github.com/ExpediaGroup/graphql-kotlin ● KGraphQL https://github.com/aPureBase/KGraphQL ● Sangria https://sangria-graphql.org/ ● Lacinia https://github.com/walmartlabs/lacinia ● Gorm GraphQL https://github.com/grails/gorm-graphql/ ● GQL https://grooviter.github.io/gql/
  • 29.
  • 30.
    Testing GraphQL @SpringBootTest @ContextConfiguration(classes =Main) class CustomerFetcherSpec extends Specification { @Autowired GraphQLSchema graphQLSchema GraphQL graphQL def setup() { graphQL = GraphQL.newGraphQL(graphQLSchema).build() } @MarcinStachniukGraphQL in Java
  • 31.
    Testing GraphQL def "shouldget customer by id"() { given: def query = """{ customer(id: "2") { … } }""" def expected = [ "customer": [ … ] ] when: def result = graphQL.execute(query) then: result.data == expected } @MarcinStachniukGraphQL in Java
  • 32.
    Versioning type Query { customer(id:String!): Customer! @deprecated(reason: "not used ...") @MarcinStachniukGraphQL in Java
  • 33.
  • 34.
    From Code Firstto Schema First migration https://github.com/mstachniuk/graphql-schema-from-introspection-generator @MarcinStachniukGraphQL in Java
  • 35.
    From Code Firstto Schema First migration alternatives ● In graphql-java - see: IntrospectionResultToSchema ● In graphql-js: const graphql = require("graphql"); const schema = require("path/to/schema.json"); const clientSchema = graphql.buildClientSchema(schema.data); const schemaString = graphql.printSchema(clientSchema); console.log(schemaString) @MarcinStachniukGraphQL in Java
  • 36.
    GraphQL downsides: N+1problem { customers { 1 call id name orders { n calls id status } } } java-dataloader ● Add async BatchLoader ● Add caching @MarcinStachniuk graphQL = GraphQL.newGraphQL(schema) .queryExecutionStrategy( new BatchedExecutionStrategy()) .build(); + @Batched in DataFetcher#get() GraphQL in Java
  • 37.
    GraphQL downsides: N+1problem If you have N + 1 problem use java-dataloader @MarcinStachniukGraphQL in Java
  • 38.
    Complex Queries fragment Friendson Friend { id name friends { id name friends { id name ... } } } @MarcinStachniuk graphQL = GraphQL.newGraphQL(schema) .instrumentation( new ChainedInstrumentation(asList( new MaxQueryComplexityInstrumentation(20), new MaxQueryDepthInstrumentation(2) ))) .build(); GraphQL in Java
  • 39.
    Complex Queries Performance killer? Defineinstrumentation! @MarcinStachniukGraphQL in Java
  • 40.
  • 41.
  • 42.
    Recommended tools: AltairGraphQL Client @MarcinStachniukGraphQL in Java https://altair.sirmuel.design/
  • 43.
    Recommended tools: Relay @MarcinStachniukGraphQLin Java @MarcinStachniuk user(...) { photo(width: "120", height: "120") } user(...) { name } user(...) { email } user(...) { name email photo(width: "120", height: "120") } GraphQL in Java
  • 44.
    Recommended tools: GraphQLschema linter https://github.com/cjoudrey/graphql-schema-linter Validate GraphQL schema definitions against a set of rules @MarcinStachniukGraphQL in Java
  • 45.
    Recommended tools: Graphqlnetwork - Chrome extension @MarcinStachniuk https://chrome.google.com/webstore/detail/graphql-network/ool bobjfnfdnpgjcaphejlodpbefhmbn GraphQL in Java
  • 46.
    Recommended tools: Graphqlnetwork - Chrome extension @MarcinStachniukGraphQL in Java
  • 47.
    More libraries andprojects related to graphql-java https://github.com/graphql-java/awesome-graphql-java @MarcinStachniukGraphQL in Java
  • 48.
    Good practices ● Alwaysgenerate GraphQL Schema and commit into your repository ● Follow conventions (e.g. Relay) ● Validate your schema ● Do not use classic code first approach to define schema @MarcinStachniukGraphQL in Java
  • 49.
  • 50.