Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

GraphQL - when REST API is to less - lessons learned

148 Aufrufe

Veröffentlicht am

Slajdy z Poznań JUG 05.03.2019

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

GraphQL - when REST API is to less - lessons learned

  1. 1. GraphQL - when REST API is not enough - lessons learned Marcin Stachniuk 5 March 2019
  2. 2. Marcin Stachniuk mstachniuk.github.io /mstachniuk/graphql-java-example @MarcinStachniuk wroclaw.jug.pl shipkit.org
  3. 3. http://segfault.events/sites/gdansk2019/abstracts/shipkit/
  4. 4. Who is using REST? @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  5. 5. REST - REpresentational State Transfer https://api.example.com/customers/123 DELETE PUT POST GET PATCH @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  6. 6. REST fixed response GET /customers/111 { "customer": { "id": "111", "name": "John Doe", "email": "john@doe.com", "company": { "id": "222" }, "orders": [ { "id": "333" }, { "id": "444" } ] } } { "customer": { "id": "111", "name": "John Doe", "email": "john@doe.com", "company": { "href": "https://api.example.com/companies/222" }, "orders": [ { "href": "https://api.example.com/orders/333" }, { "href": "https://api.example.com/orders/444" } ] } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  7. 7. REST consequences: several roundtrips @MarcinStachniukGraphQL - when REST API is not enough - lessons learned U nderFetching
  8. 8. REST response with nested data GET /customers/111 { "customer": { "id": "111", "name": "John Doe", "email": "john@doe.com", "company": { "id": "222", "name": "My Awesome Corporation", "website": "MyAwesomeCorporation.com" }, "orders": [ { "id": "333", "status": "delivered", "items": [ { "id": "555", "name": "Silver Bullet", "amount": "42", "price": "10000000", "currency": "USD", "producer": { "id": "777", "name": "Lorem Ipsum", "website": "LoremIpsum.com" } } ] }, { "id": "444", "name": "Golden Hammer", "amount": "5", "price": "10000", "currency": "USD", "producer": { ... } } ] } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned O verFetching
  9. 9. REST response with nested data and limit fields GET /customers/111?fields=name,company/*,orders.status,orders.items(name,producer/name) { "customer": { "id": "111", "name": "John Doe", "email": "john@doe.com", "company": { "id": "222", "name": "My Awesome Corporation", "website": "MyAwesomeCorporation.com" }, "orders": [ { "id": "333", "status": "delivered", "items": [ { "id": "555", "name": "Silver Bullet", "amount": "42", "price": "10000000", "currency": "USD", "producer": { "id": "777", "name": "Lorem Ipsum", "website": "LoremIpsum.com" } } ] }, { "id": "444", "name": "Golden Hammer", "amount": "5", "price": "10000", "currency": "USD", "producer": { ... } } ] } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  10. 10. REST response with projection GET /customers/111?projection=customerPreview { "customer": { "id": "111", "name": "John Doe", "email": "john@doe.com", "company": { "id": "222", "name": "My Awesome Corporation", "website": "MyAwesomeCorporation.com" }, "orders": [ { "id": "333", "status": "delivered", "items": [ { "id": "555", "name": "Silver Bullet", "amount": "42", "price": "10000000", "currency": "USD", "producer": { "id": "777", "name": "Lorem Ipsum", "website": "LoremIpsum.com" } } ] }, { "id": "444", "name": "Golden Hammer", "amount": "5", "price": "10000", "currency": "USD", "producer": { ... } } ] } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  11. 11. Different clients - different needs /web /iphone /android /tv Application Web iPhone Android TV RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  12. 12. Different clients - different needs /web /iphone /android /tv Application Web iPhone Android TV Content-Type: application/vnd.myawesomecorporation.com+v1+web+json iphone android tv RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  13. 13. Different clients - different needs /web /iphone /android /tv Application Web iPhone Android TV Content-Type: application/vnd.myawesomecorporation.com+v1+web+json iphone android tv RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  14. 14. REST contract is rigid @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  15. 15. Platform Architecture Platform App 1 App 2 Customer App X... @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  16. 16. REST API Versioning GET /v2/customers/111RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned GET /customers/111?v=v2RE ST GET /customers/111 Accept header: application/vnd.myapp.2+jsonRE ST GET /customers/111 Custom header: x-ms-version:2RE ST ● How to migrate? ● How to document? ● Is versionless API possible? ● What about continuous evolution of API?
  17. 17. Modularisation at UI @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  18. 18. New Frontend Framework ReactJS Relay GraphQL TypeScript @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  19. 19. GraphQL ● Graph Query Language ● Published by Facebook in 2015 ● Growth from Facebook Graph API ● Reference implementation in JavaScript @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  20. 20. GraphQL main concepts ● One endpoint for all operations ● Always define in request what you need ● Queries, Mutations and Subscriptions ● Defined by schema @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  21. 21. Data is a graph @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  22. 22. GraphQL time for demo ● Fragments ● Aliases ● Directives ● Interfaces ● Unions @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● Query ● Syntax Error ● Mutation ● Operation name ● Variables
  23. 23. GraphQL Simple API GET /customers/2?fields=id,name,email type Customer { #fields with ! are not null id: ID! name: String! email: String! } type Query { customer(id: String!): Customer! } { "data": { "customer": { "id": "2", "name": "name", "email": "a@b.com" } } } { customer(id: "2") { id name email } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  24. 24. GraphQL Bad Request GET /custo!@#$ -> 404 { "data": null, "errors": [ { "message": "Invalid Syntax", "locations": [ { "line": 2, "column": 8 } ], "errorType": "InvalidSyntax", "path": null, "extensions": null } ] } { custo!@#$ } RE ST http.cat/200 @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  25. 25. Go back to the roots @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  26. 26. GraphQL Simple API GET /customers/2?fields=id,name,email,company(id,name) type Customer { id: ID! name: String! email: String! company: Company } type Company { id: ID! name: String! website: String! } type Query { customer(id: String!): Customer! } { "data": { "customer": { "id": "2", "name": "name", "email": "a@b.com", "company": { "id": "211", "name": "Company Corp." } } } } { customer(id: "2") { id name email company { id name } } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  27. 27. 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 - when REST API is not enough - lessons learned
  28. 28. 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 - when REST API is not enough - lessons learned
  29. 29. GraphQL mutations + operation name mutation crCust { createCustomer(input: { name: "MyName" email: "me@me.com" clientMutationId: "123" }) { customer { id } clientMutationId } } { "data": { "createCustomer": { "customer": { "id": "40", }, "clientMutationId": "123" } } } POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned mutation { createCustomer(input: { name: "MyName" email: "me@me.com" clientMutationId: "123" }) { customer { id } clientMutationId } }
  30. 30. GraphQL Variables { "data": { "createCustomer": { "customer": { "id": "40", }, "clientMutationId": "123" } } } POST /customers PUT /customers/123 DELETE /customers/123 PATCH /customers/123 mutation crCust { createCustomer(input: { name: "MyName" email: "me@me.com" clientMutationId: "123" }) { customer { id } clientMutationId } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned mutation crCust ($input: CreateCustInput) { createCustomer(input: $input) { customer { id } clientMutationId } } { "input": { "name": "MyName 2", "email": "me2@me.com", "clientMutationId": "123" } }
  31. 31. GraphQL Aliases GET /customers/2?fields=id,name,email + { "data": { "cust1": { "id": "2", "name": "name", "email": "a@b.com" }, "cust2": { "id": "3", "name": "John Doe", "email": "john@doe.com" } } } query get2Cust { cust1: customer(id: "2") { id name email } cust2: customer(id: "3") { id name email } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned GET /customers/3?fields=id,name,emailRE ST
  32. 32. GraphQL Fragment GET /customers/2?fields=id,name,email + { "data": { "cust1": { "id": "2", "name": "name", "email": "a@b.com" }, "cust2": { "id": "3", "name": "John Doe", "email": "john@doe.com" } } } query get2Cust { cust1: customer(id: "2") { ... frag1 } cust2: customer(id: "3") { ... frag1 } } fragment frag1 on Customer { id name email } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned GET /customers/3?fields=id,name,emailRE ST
  33. 33. GraphQL Directive GET /customers/2?fields=id,name { "data": { "customer": { "id": "2", "name": "name" } } } query getCust ($showEmail: Boolean!) { customer(id: "2") { id name email @include(if: $showEmail) } } { "showEmail": false } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  34. 34. 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 a", "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 - when REST API is not enough - lessons learned
  35. 35. GraphQL Union GET /search?input=a?fields=typename,name type Query { search(input: String): [SearchResult] } union SearchResult = Customer | Admin | Moderator { "data": { "search": [ { "__typename": "Customer", "name": "name" }, { "__typename": "Admin", "name": "Admin a" }, { "__typename": "Moderator", "name": "Moderator" }]}} query searchSmth { search(input: "a") { __typename ... on Customer { name } ... on Admin { name } ... on Moderator { name } } } RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  36. 36. GraphQL ● Graph Query Language ● Published by Facebook in 2015 ● Growth from Facebook Graph API ● Reference implementation in JavaScript ● First version of Java Library: 18 Jul 2015 https://github.com/graphql-java/graphql-java ● First usage: 21 Sep 2015 @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  37. 37. Lessons Learned #1 Never add a library to your project few days after init release @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● No community ● A lot of bugs ● Bad documentation ● Strict following reference implementation and specification DO NOT TRY THIS AT WORK
  38. 38. 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 - when REST API is not enough - lessons learned
  39. 39. 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 - when REST API is not enough - lessons learned
  40. 40. 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 } } } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned 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); } }
  41. 41. 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 - when REST API is not enough - lessons learned
  42. 42. 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 - when REST API is not enough - lessons learned
  43. 43. 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 - when REST API is not enough - lessons learned
  44. 44. 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 - when REST API is not enough - lessons learned
  45. 45. Abstraction over GraphQL Java Our abstraction Data Fetcher 2 Inputs mapping to objects Schema definition Pagination ... Data Fetcher 1 Data Fetcher N... @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  46. 46. Lessons Learned #2 Abstraction is not good if you don’t understand how it works under the hood @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● Copy paste errors ● Wrong usage ● Hard to update to new version
  47. 47. GraphQL type system How to define your schema? @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  48. 48. 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(); } 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 - when REST API is not enough - lessons learned
  49. 49. Code First approach - How to build Introspection query Introspection response Replace Relay definitions @MarcinStachniukGraphQL - when REST API is not enough - lessons learned Typescript relay plugin
  50. 50. 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 - when REST API is not enough - lessons learned
  51. 51. Schema First approach - project building diagram model.graphqls @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  52. 52. GraphQL SPQR @MarcinStachniukGraphQL - when REST API is not enough - lessons learned https://github.com/leangen/graphql-spqr SPQR - Senatus Populusque Romanus
  53. 53. SPQR approach public class UserService { @GraphQLQuery(name = "user") public User getById(@GraphQLArgument(name = "id") Integer id) { //... } } public class Customer { //... @GraphQLQuery(name = "name", description = "A person's name") public String getName() { return name; } } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  54. 54. Lessons Learned #3 Code First Approach is the worst Schema First Approach: ● Easy to maintain and understand ● Helps organise work ● Demo schema is 3x smaller GraphQL SPQR - ? Code First approach: ● Hard to maintain ● It was the only way at the beginning to define a schema ● No possibility to mix both ● No easy way to migrate to Schema First @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  55. 55. From Code First to Schema First migration https://github.com/mstachniuk/graphql-schema-from-introspection-generator @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  56. 56. GraphQL - How to define pagination, filtering, sorting? Pagination: ● before, after ● offset, limit Filtering: ● filter(name: “Bob” email: “%@gmail.com”) ● filter: { OR: [{ email: “%@gmail.com” }] }, name: “Bob” } Sorting: ● orderBy: ASC, DESC ● sort: NEWEST, IMPORTANCE @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  57. 57. Lessons Learned #4 GraphQL is not full query language @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● Flexibility ● Less common conventions ● Dgraph.io created GraphQL+-
  58. 58. GraphQL downsides @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  59. 59. GraphQL downsides: N+1 problem { customers { 1 call id name orders { n calls id status } } } java-dataloader ● Add async BatchLoader ● Add caching @MarcinStachniukGraphQL - when REST API is not enough - lessons learned graphQL = GraphQL.newGraphQL(schema) .queryExecutionStrategy( new BatchedExecutionStrategy()) .build(); + @Batched in DataFetcher#get()
  60. 60. Lessons Learned #5 If you have N + 1 problem use java-dataloader @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  61. 61. Complex Queries fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ... } } } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned graphQL = GraphQL.newGraphQL(schema) .instrumentation( new ChainedInstrumentation(asList( new MaxQueryComplexityInstrumentation(200), new MaxQueryDepthInstrumentation(20) ))) .build();
  62. 62. Lessons Learned #6 Performance killer? Define instrumentation! @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  63. 63. GraphQL downsides: Bad GraphQL API definition - examples { customer(id: "2") { … } customerFull(id: "2") { … } customerFull2(id: "2") { … } customerWithDetails(id: "2") { … } ... } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned { customer(id: "2") { id name orders { id status } } }
  64. 64. GraphQL downsides: Bad GraphQL API definition - examples query getUser { user(id: "123") { ... on Admin { id superAdmin } ... on Moderator { id permissions } } } { admin(id: "123") { id superAdmin } moderator(id: "123") { id permissions } } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  65. 65. GraphQL downsides: Bad GraphQL API definition - examples { orders (input: { status: "NEW" first: "2" offset: "3" }, first: "1", offset: "3") { Items { … } } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  66. 66. Lessons Learned #7 Thinking shift is a key @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● Let’s think in graphs and NOT in endpoints / resources / entities / DTOs ● Bad design of our API
  67. 67. GraphQL Testing @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  68. 68. Testing GraphQL @SpringBootTest @ContextConfiguration(classes = Main) class CustomerFetcherSpec extends Specification { @Autowired GraphQLSchema graphQLSchema GraphQL graphQL def setup() { graphQL = GraphQL.newGraphQL(graphQLSchema).build() } @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  69. 69. 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 - when REST API is not enough - lessons learned
  70. 70. Lessons Learned #8 Testing is easy @MarcinStachniukGraphQL - when REST API is not enough - lessons learned Trap Adventure 2 - "The Hardest Retro Game"
  71. 71. Versioning @MarcinStachniukGraphQL - when REST API is not enough - lessons learned GraphQL takes a strong opinion on avoiding versioning by providing the tools for the continuous evolution of a GraphQL schema. Src: https://graphql.org/learn/best-practices/#versioning
  72. 72. Versioning type Query { customer(id: String!): Customer! @deprecated(reason: "not used ...") @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  73. 73. Lessons Learned #9 Versioning is not a problem @MarcinStachniukGraphQL - when REST API is not enough - lessons learned GET /v2/customers/111RE ST GET /customers/111?v=v2RE ST GET /customers/111 Accept header: application/vnd.myapp.2+jsonRE ST GET /customers/111 Custom header: x-ms-version:2RE ST Versioning problem is different
  74. 74. Tools @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  75. 75. Tools GraphiQL @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  76. 76. Tools Relay @MarcinStachniukGraphQL - when REST API is not enough - lessons learned @MarcinStachniukGraphQL - when REST API is not enough - lessons learned user(...) { photo(width: "120", height: "120") } user(...) { name } user(...) { email } user(...) { name email photo(width: "120", height: "120") }
  77. 77. Distelli/graphql-apigen https://github.com/Distelli/graphql-apigen Generate Java APIs with GraphQL Schemas in order to facilitate "schema first" development. @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  78. 78. GraphQL Java Tools https://github.com/graphql-java/graphql-java-tools Map GraphQL schema to POJOs @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  79. 79. More libraries and projects related to graphql-java https://github.com/graphql-java/awesome-graphql-java @MarcinStachniukGraphQL - when REST API is not enough - lessons learned ● Generates a GraphQL schema from a JDBC data source ● Annotations-based syntax for GraphQL schema definition ● JPA Implementation of GraphQL (builds on graphql-java) ● And more examples
  80. 80. Apollo @MarcinStachniukGraphQL - when REST API is not enough - lessons learned Build a universal GraphQL API on top of your existing REST APIs, so you can ship new application features fast without waiting on backend changes.
  81. 81. Lessons Learned #10 Tooling is nice @MarcinStachniukGraphQL - when REST API is not enough - lessons learned now
  82. 82. Summary GraphQL Pros: ● Nice alternative to REST ● It can be used together with REST ● Good integration with Relay / ReactJS ● You get exactly what you want to get ● Good for API with different clients ● Good to use on top of existing API ● Self documented ● Easy testing ● Nice tooling ● Thin layer GraphQL Cons: ● High entry barrier ● Hard to return simple Map ● Not well know (yet) ● Performance overhead ● A lot of similar code to write ● No Operation Idempotency ● No Cache & Security ● No Authorisation ● Always HTTP 200 - unsupported tools @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  83. 83. Nothing is a silver bullet @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  84. 84. Q&A @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  85. 85. GraphQL - when REST API is not enough - lessons learned Marcin Stachniuk 5 March 2019 Thank you!

×