SlideShare a Scribd company logo
1 of 86
Download to read offline
GraphQL - when REST API is
not enough - lessons learned
Marcin Stachniuk
5 March 2019
Marcin Stachniuk
mstachniuk.github.io
/mstachniuk/graphql-java-example
@MarcinStachniuk
wroclaw.jug.pl
shipkit.org
http://segfault.events/sites/gdansk2019/abstracts/shipkit/
Who is using REST?
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
REST - REpresentational State Transfer
https://api.example.com/customers/123
DELETE
PUT
POST
GET
PATCH
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
REST consequences: several roundtrips
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
U
nderFetching
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
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
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
Different clients - different needs
/web /iphone /android /tv
Application
Web iPhone Android TV
RE
ST
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
REST contract is rigid
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
Platform Architecture
Platform
App 1 App 2
Customer
App X...
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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?
Modularisation at UI
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
New Frontend Framework
ReactJS
Relay GraphQL
TypeScript
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
Data is a graph
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
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
Go back to the roots
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
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
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
}
}
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"
}
}
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
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
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
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
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
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
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
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
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
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);
}
}
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
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
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
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
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
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
GraphQL type system
How to define your schema?
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
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
Schema First approach - project building diagram
model.graphqls
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
GraphQL SPQR
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
https://github.com/leangen/graphql-spqr
SPQR - Senatus Populusque Romanus
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
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
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
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
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+-
GraphQL downsides
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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()
Lessons Learned #5
If you have N + 1 problem
use java-dataloader
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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();
Lessons Learned #6
Performance killer?
Define instrumentation!
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
}
}
}
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
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
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
GraphQL Testing
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
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
Lessons Learned #8
Testing is easy
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
Trap Adventure 2 - "The Hardest Retro Game"
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
Versioning
type Query {
customer(id: String!): Customer! @deprecated(reason: "not used ...")
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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
Tools
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
Tools GraphiQL
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
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")
}
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
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
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
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.
Lessons Learned #10
Tooling is nice
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
now
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
Nothing is a silver bullet
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
Q&A
@MarcinStachniukGraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is
not enough - lessons learned
Marcin Stachniuk
5 March 2019
Thank
you!

More Related Content

What's hot

Novedades de la API - Leandro Bertalot
Novedades de la API - Leandro BertalotNovedades de la API - Leandro Bertalot
Novedades de la API - Leandro Bertalotfsolari
 
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDBMongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDBMongoDB
 
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...MongoDB
 
[@IndeedEng] Building Indeed Resume Search
[@IndeedEng] Building Indeed Resume Search[@IndeedEng] Building Indeed Resume Search
[@IndeedEng] Building Indeed Resume Searchindeedeng
 
Pascarello_Investigating JavaScript and Ajax Security
Pascarello_Investigating JavaScript and Ajax SecurityPascarello_Investigating JavaScript and Ajax Security
Pascarello_Investigating JavaScript and Ajax Securityamiable_indian
 
Sigfox API Documentation
Sigfox API DocumentationSigfox API Documentation
Sigfox API DocumentationRod Hemphill
 

What's hot (8)

Novedades de la API - Leandro Bertalot
Novedades de la API - Leandro BertalotNovedades de la API - Leandro Bertalot
Novedades de la API - Leandro Bertalot
 
MongoDB Meetup
MongoDB MeetupMongoDB Meetup
MongoDB Meetup
 
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDBMongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
MongoDB.local DC 2018: Tutorial - Data Analytics with MongoDB
 
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...
MongoDB .local Chicago 2019: Best Practices for Working with IoT and Time-ser...
 
[@IndeedEng] Building Indeed Resume Search
[@IndeedEng] Building Indeed Resume Search[@IndeedEng] Building Indeed Resume Search
[@IndeedEng] Building Indeed Resume Search
 
Pascarello_Investigating JavaScript and Ajax Security
Pascarello_Investigating JavaScript and Ajax SecurityPascarello_Investigating JavaScript and Ajax Security
Pascarello_Investigating JavaScript and Ajax Security
 
Sigfox API Documentation
Sigfox API DocumentationSigfox API Documentation
Sigfox API Documentation
 
Ajax Lecture Notes
Ajax Lecture NotesAjax Lecture Notes
Ajax Lecture Notes
 

Similar to GraphQL - when REST API is to less - lessons learned

GraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedGraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedMarcinStachniuk
 
GraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedGraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedMarcinStachniuk
 
BruJUG Brussels GraphQL when RESR API is to less - lessons learned
BruJUG Brussels GraphQL when RESR API is to less - lessons learnedBruJUG Brussels GraphQL when RESR API is to less - lessons learned
BruJUG Brussels GraphQL when RESR API is to less - lessons learnedMarcinStachniuk
 
GraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learnedGraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learnedMarcinStachniuk
 
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademy
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademyGraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademy
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademyMarcinStachniuk
 
The Serverless GraphQL Backend Architecture
The Serverless GraphQL Backend ArchitectureThe Serverless GraphQL Backend Architecture
The Serverless GraphQL Backend ArchitectureNikolas Burk
 
Overview of GraphQL & Clients
Overview of GraphQL & ClientsOverview of GraphQL & Clients
Overview of GraphQL & ClientsPokai Chang
 
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScript
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScriptMongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScript
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScriptMongoDB
 
GraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized ageGraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized ageBartosz Sypytkowski
 
Building Serverless GraphQL Backends
Building Serverless GraphQL BackendsBuilding Serverless GraphQL Backends
Building Serverless GraphQL BackendsNikolas Burk
 
Wroclaw GraphQL - GraphQL in Java
Wroclaw GraphQL - GraphQL in JavaWroclaw GraphQL - GraphQL in Java
Wroclaw GraphQL - GraphQL in JavaMarcinStachniuk
 
Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk   Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk OdessaJS Conf
 
Connecting the Dots: Kong for GraphQL Endpoints
Connecting the Dots: Kong for GraphQL EndpointsConnecting the Dots: Kong for GraphQL Endpoints
Connecting the Dots: Kong for GraphQL EndpointsJulien Bataillé
 
Rest vs graphql - why choose
Rest vs graphql - why chooseRest vs graphql - why choose
Rest vs graphql - why chooseTally Barak
 
Next-generation API Development with GraphQL and Prisma
Next-generation API Development with GraphQL and PrismaNext-generation API Development with GraphQL and Prisma
Next-generation API Development with GraphQL and PrismaNikolas Burk
 
Real-time search in Drupal with Elasticsearch @Moldcamp
Real-time search in Drupal with Elasticsearch @MoldcampReal-time search in Drupal with Elasticsearch @Moldcamp
Real-time search in Drupal with Elasticsearch @MoldcampAlexei Gorobets
 
LJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersLJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersChristopher Batey
 
How to Leverage APIs for SEO #TTTLive2019
How to Leverage APIs for SEO #TTTLive2019How to Leverage APIs for SEO #TTTLive2019
How to Leverage APIs for SEO #TTTLive2019Paul Shapiro
 

Similar to GraphQL - when REST API is to less - lessons learned (20)

GraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedGraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learned
 
GraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learnedGraphQL - when REST API is to less - lessons learned
GraphQL - when REST API is to less - lessons learned
 
BruJUG Brussels GraphQL when RESR API is to less - lessons learned
BruJUG Brussels GraphQL when RESR API is to less - lessons learnedBruJUG Brussels GraphQL when RESR API is to less - lessons learned
BruJUG Brussels GraphQL when RESR API is to less - lessons learned
 
GraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learnedGraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learned
 
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademy
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademyGraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademy
GraphQL - Piękne API w Twojej Aplikacji - KrakowGraphAcademy
 
The Serverless GraphQL Backend Architecture
The Serverless GraphQL Backend ArchitectureThe Serverless GraphQL Backend Architecture
The Serverless GraphQL Backend Architecture
 
Overview of GraphQL & Clients
Overview of GraphQL & ClientsOverview of GraphQL & Clients
Overview of GraphQL & Clients
 
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScript
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScriptMongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScript
MongoDB World 2019: Building a GraphQL API with MongoDB, Prisma, & TypeScript
 
GraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized ageGraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized age
 
Building Serverless GraphQL Backends
Building Serverless GraphQL BackendsBuilding Serverless GraphQL Backends
Building Serverless GraphQL Backends
 
Wroclaw GraphQL - GraphQL in Java
Wroclaw GraphQL - GraphQL in JavaWroclaw GraphQL - GraphQL in Java
Wroclaw GraphQL - GraphQL in Java
 
Elasticsearch
ElasticsearchElasticsearch
Elasticsearch
 
Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk   Specification-Driven Development of REST APIs by Alexander Zinchuk
Specification-Driven Development of REST APIs by Alexander Zinchuk
 
Connecting the Dots: Kong for GraphQL Endpoints
Connecting the Dots: Kong for GraphQL EndpointsConnecting the Dots: Kong for GraphQL Endpoints
Connecting the Dots: Kong for GraphQL Endpoints
 
React & GraphQL
React & GraphQLReact & GraphQL
React & GraphQL
 
Rest vs graphql - why choose
Rest vs graphql - why chooseRest vs graphql - why choose
Rest vs graphql - why choose
 
Next-generation API Development with GraphQL and Prisma
Next-generation API Development with GraphQL and PrismaNext-generation API Development with GraphQL and Prisma
Next-generation API Development with GraphQL and Prisma
 
Real-time search in Drupal with Elasticsearch @Moldcamp
Real-time search in Drupal with Elasticsearch @MoldcampReal-time search in Drupal with Elasticsearch @Moldcamp
Real-time search in Drupal with Elasticsearch @Moldcamp
 
LJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersLJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java Developers
 
How to Leverage APIs for SEO #TTTLive2019
How to Leverage APIs for SEO #TTTLive2019How to Leverage APIs for SEO #TTTLive2019
How to Leverage APIs for SEO #TTTLive2019
 

More from MarcinStachniuk

[WroclawJUG] Continuous Delivery in OSS using Shipkit
[WroclawJUG] Continuous Delivery in OSS using Shipkit[WroclawJUG] Continuous Delivery in OSS using Shipkit
[WroclawJUG] Continuous Delivery in OSS using ShipkitMarcinStachniuk
 
Continuous Delivery in OSS using Shipkit.org
Continuous Delivery in OSS using Shipkit.orgContinuous Delivery in OSS using Shipkit.org
Continuous Delivery in OSS using Shipkit.orgMarcinStachniuk
 
Java Web Start – jak żyć z tą dziwną technologią
Java Web Start – jak żyć z tą dziwną technologiąJava Web Start – jak żyć z tą dziwną technologią
Java Web Start – jak żyć z tą dziwną technologiąMarcinStachniuk
 
Zarządzanie zmianami w schemacie relacyjnych baz danych
Zarządzanie zmianami w schemacie relacyjnych baz danychZarządzanie zmianami w schemacie relacyjnych baz danych
Zarządzanie zmianami w schemacie relacyjnych baz danychMarcinStachniuk
 
Inicjatywa NoSQL na przykładzie db4o
Inicjatywa NoSQL na przykładzie db4oInicjatywa NoSQL na przykładzie db4o
Inicjatywa NoSQL na przykładzie db4oMarcinStachniuk
 
Automatic mechanism data migration between relational and object database
Automatic mechanism data migration between relational and object databaseAutomatic mechanism data migration between relational and object database
Automatic mechanism data migration between relational and object databaseMarcinStachniuk
 
Zastosowanie obiektowych baz danych na przykładzie db4o
Zastosowanie obiektowych baz danych na przykładzie db4oZastosowanie obiektowych baz danych na przykładzie db4o
Zastosowanie obiektowych baz danych na przykładzie db4oMarcinStachniuk
 
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017MarcinStachniuk
 
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...MarcinStachniuk
 
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...MarcinStachniuk
 
Continuous delivery w projekcie open source - Marcin Stachniuk
Continuous delivery w projekcie open source - Marcin StachniukContinuous delivery w projekcie open source - Marcin Stachniuk
Continuous delivery w projekcie open source - Marcin StachniukMarcinStachniuk
 
Zarządzanie zamianami w relacyjnych bazach danych
Zarządzanie zamianami w relacyjnych bazach danychZarządzanie zamianami w relacyjnych bazach danych
Zarządzanie zamianami w relacyjnych bazach danychMarcinStachniuk
 
Nowości w Javie 8 okiem programisty
Nowości w Javie 8 okiem programistyNowości w Javie 8 okiem programisty
Nowości w Javie 8 okiem programistyMarcinStachniuk
 
Liquibase - Zarządzanie zmianami w relacyjnych bazach danych
Liquibase - Zarządzanie zmianami w relacyjnych bazach danychLiquibase - Zarządzanie zmianami w relacyjnych bazach danych
Liquibase - Zarządzanie zmianami w relacyjnych bazach danychMarcinStachniuk
 
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...MarcinStachniuk
 
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...MarcinStachniuk
 
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.MarcinStachniuk
 
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowania
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowaniaPodstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowania
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowaniaMarcinStachniuk
 

More from MarcinStachniuk (19)

[WroclawJUG] Continuous Delivery in OSS using Shipkit
[WroclawJUG] Continuous Delivery in OSS using Shipkit[WroclawJUG] Continuous Delivery in OSS using Shipkit
[WroclawJUG] Continuous Delivery in OSS using Shipkit
 
Continuous Delivery in OSS using Shipkit.org
Continuous Delivery in OSS using Shipkit.orgContinuous Delivery in OSS using Shipkit.org
Continuous Delivery in OSS using Shipkit.org
 
Java Web Start – jak żyć z tą dziwną technologią
Java Web Start – jak żyć z tą dziwną technologiąJava Web Start – jak żyć z tą dziwną technologią
Java Web Start – jak żyć z tą dziwną technologią
 
Zarządzanie zmianami w schemacie relacyjnych baz danych
Zarządzanie zmianami w schemacie relacyjnych baz danychZarządzanie zmianami w schemacie relacyjnych baz danych
Zarządzanie zmianami w schemacie relacyjnych baz danych
 
Inicjatywa NoSQL na przykładzie db4o
Inicjatywa NoSQL na przykładzie db4oInicjatywa NoSQL na przykładzie db4o
Inicjatywa NoSQL na przykładzie db4o
 
Automatic mechanism data migration between relational and object database
Automatic mechanism data migration between relational and object databaseAutomatic mechanism data migration between relational and object database
Automatic mechanism data migration between relational and object database
 
Zastosowanie obiektowych baz danych na przykładzie db4o
Zastosowanie obiektowych baz danych na przykładzie db4oZastosowanie obiektowych baz danych na przykładzie db4o
Zastosowanie obiektowych baz danych na przykładzie db4o
 
Wprowadzenie do J2ME
Wprowadzenie do J2MEWprowadzenie do J2ME
Wprowadzenie do J2ME
 
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017
Continuous Delivery w projekcie Open Source - Marcin Stachniuk - DevCrowd 2017
 
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...
Java Web Start czyli jak żyć z tą dziwną technologią? & Continuous Delivery w...
 
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...
Java Web Start czyli jak żyć z tą dziwną technologią & Continuous Delivery w ...
 
Continuous delivery w projekcie open source - Marcin Stachniuk
Continuous delivery w projekcie open source - Marcin StachniukContinuous delivery w projekcie open source - Marcin Stachniuk
Continuous delivery w projekcie open source - Marcin Stachniuk
 
Zarządzanie zamianami w relacyjnych bazach danych
Zarządzanie zamianami w relacyjnych bazach danychZarządzanie zamianami w relacyjnych bazach danych
Zarządzanie zamianami w relacyjnych bazach danych
 
Nowości w Javie 8 okiem programisty
Nowości w Javie 8 okiem programistyNowości w Javie 8 okiem programisty
Nowości w Javie 8 okiem programisty
 
Liquibase - Zarządzanie zmianami w relacyjnych bazach danych
Liquibase - Zarządzanie zmianami w relacyjnych bazach danychLiquibase - Zarządzanie zmianami w relacyjnych bazach danych
Liquibase - Zarządzanie zmianami w relacyjnych bazach danych
 
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
 
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
Poznaj lepiej swoje srodowisko programistyczne i zwieksz swoja produktywnosc ...
 
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.
Upieksz swoje testy! Testowanie jednostkowe dla sredniozaawansowanych.
 
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowania
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowaniaPodstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowania
Podstawy tworzenia gier w J2ME, dla początkujących adeptów sztuki programowania
 

Recently uploaded

Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningVitsRangannavar
 
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝soniya singh
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutionsmonugehlot87
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 

Recently uploaded (20)

Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learning
 
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutions
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 

GraphQL - when REST API is to less - lessons learned

  • 1. GraphQL - when REST API is not enough - lessons learned Marcin Stachniuk 5 March 2019
  • 4.
  • 5. Who is using REST? @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 6. REST - REpresentational State Transfer https://api.example.com/customers/123 DELETE PUT POST GET PATCH @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 7. 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
  • 8. REST consequences: several roundtrips @MarcinStachniukGraphQL - when REST API is not enough - lessons learned U nderFetching
  • 9. 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
  • 10. 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
  • 11. 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
  • 12. Different clients - different needs /web /iphone /android /tv Application Web iPhone Android TV RE ST @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 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. 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
  • 15. REST contract is rigid @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 16. Platform Architecture Platform App 1 App 2 Customer App X... @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 17. 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?
  • 18. Modularisation at UI @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 19. New Frontend Framework ReactJS Relay GraphQL TypeScript @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 20. 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
  • 21. 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
  • 22. Data is a graph @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 23. 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
  • 24. 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
  • 25. 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
  • 26. Go back to the roots @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 27. 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
  • 28. 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
  • 29. 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
  • 30. 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 } }
  • 31. 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" } }
  • 32. 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
  • 33. 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
  • 34. 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
  • 35. 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
  • 36. 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
  • 37. 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
  • 38. 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
  • 39. 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
  • 40. 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
  • 41. 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); } }
  • 42. 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
  • 43. 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
  • 44. 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
  • 45. 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
  • 46. 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
  • 47. 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
  • 48. GraphQL type system How to define your schema? @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 49. 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
  • 50. 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
  • 51. 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
  • 52. Schema First approach - project building diagram model.graphqls @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 53. GraphQL SPQR @MarcinStachniukGraphQL - when REST API is not enough - lessons learned https://github.com/leangen/graphql-spqr SPQR - Senatus Populusque Romanus
  • 54. 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
  • 55. 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
  • 56. 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
  • 57. 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
  • 58. 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+-
  • 59. GraphQL downsides @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 60. 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()
  • 61. Lessons Learned #5 If you have N + 1 problem use java-dataloader @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 62. 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();
  • 63. Lessons Learned #6 Performance killer? Define instrumentation! @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 64. 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 } } }
  • 65. 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
  • 66. 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
  • 67. 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
  • 68. GraphQL Testing @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 69. 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
  • 70. 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
  • 71. Lessons Learned #8 Testing is easy @MarcinStachniukGraphQL - when REST API is not enough - lessons learned Trap Adventure 2 - "The Hardest Retro Game"
  • 72. 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
  • 73. Versioning type Query { customer(id: String!): Customer! @deprecated(reason: "not used ...") @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 74. 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
  • 75. Tools @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 76. Tools GraphiQL @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 77. 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") }
  • 78. 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
  • 79. 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
  • 80. 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
  • 81. 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.
  • 82. Lessons Learned #10 Tooling is nice @MarcinStachniukGraphQL - when REST API is not enough - lessons learned now
  • 83. 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
  • 84. Nothing is a silver bullet @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 85. Q&A @MarcinStachniukGraphQL - when REST API is not enough - lessons learned
  • 86. GraphQL - when REST API is not enough - lessons learned Marcin Stachniuk 5 March 2019 Thank you!