Graphql, REST and Apollo

Christoffer Noring
Christoffer NoringFullstack Developer at McKinsey ( Google Developer Expert in web technologies and Telerik Developer Expert )
GraphQL vs REST
Chris Noring
Google Developer Expert
Digital McKinsey
@chris _noring
Why should I care
Presentation Structure
How to wrap an API in GraphQL
REST GraphQL comparison
Why should I care?
Problem 1
Growing end points
GET /user
POST /user
PUT /user
DELETE /user
GET /user/query..
Products
New resources needs to be added
Orders
Categories
…
Number of endpoints becomes really large, really fast
Resources needs to look different if we are dealing
with mobile or desktop
GET /users/1
data : {
firstname : ‘chris’,
}
GET /mobile/users/1
data : {
firstname : ‘chris’,
region : { url : ‘/regions/1’ }
}
Only the linkEagerly loaded
region : {
id : 1,
city : ‘Warsaw’,
country : ‘Poland’
}
Problem 2
Need for a unified way of asking for data
Twitter
Reddit
Github
Data Service
GraphQL
runtime
Frontend Query
Problem 3
Not the output I want
Mismatch in response
Mapping is needed
{ “data” : {
“name”,
“address” : “Warsaw”
}
}
class Person {
surname: string;
city: string,
}
Need to rename to avoid remapping in Frontend
Might need several endpoints
class Person {
fname : string;
surname: string;
address: string,
friends : [],
records : []
}
Might lead to multiple requests
api/people/1
api/people/1/friends
api/people/1/records
Solution
GraphQL
Lets the client specify what they need,
“content negotiation”
Easier to aggregate data from many sources
Uses a type system to describe data
Application layer query language
Multiple resources in a single request
Open sourced by Facebook in 2015
Main building blocks
Schemas
Queries
Resolvers
Mutations
How to Query
{
resource {
column,
column2
}
}
Query
data : {
resource {
column,
column2
}
}
Response
Query example
Given these resources
User ( id, name, age, address, orders) Order ( id, created, items )
OrderItem ( id, product, Quantity, Price ) Product ( id, Name )
Users {
name,
orders {
created,
items {
Quantity,
Price,
product {
Name
}
}
}
}
data : [{
‘John’,
orders : [{
‘2017-01-01’,
items : [{
Quantity : 3,
Price : 110,
Product : { Name : ‘DVD’ }
}]
}]
}]
Query with parameter
Users( email : ‘chris@chris.com’ ) {
…
}
SELECT …
FROM resource
WHERE email =‘chris@chris.com’
Query with operators
Users( email__contains : ‘chris’ ) {
}
is, not, lt, lte, gt, gte, contains, icontains, startswith, endswith,
iendswith, like, ilike
GraphQL runtime
Define a schema
Resolving a query
Building a runtime is about the following
Define queries Define mutators
Create
Update
Delete
Runtime: hello world
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} from 'graphql';
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world';
}
}
}
})
});
Define a schema
Define a query
Define a queryable field
Respond/Resolve what to answer
var query = '{ hello }';
graphql(schema, query).then(result => {
// prints { data : { “hello” : “world” } }
console.log(result);
});
Do the query
Where to go from here?
A more advanced schema
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world';
}
},
human : {
type : humanType,
resolve : () => getHuman()
}
}
})
});
function getHuman() {
return Promise.resolve(
return {
id : ‘1’,
description : ‘ a description’,
name : ‘john’
})
}
Our resolver should call
a db ideally
Resolver function
var humanType = new GraphQLObjectType({
name : 'Human',
fields : () => ({
id: { type: GraphQLString },
description : { type : GraphQLString },
name : { type : GraphQLString } }),
})
}
Complex type
var query = '{ human { name, description } }';
graphql(schema, query).then(result => {
// prints { data : { “human” : { “name” : “john”, “description” : “a description” } } }
console.log(result);
});
Do the query
Define and query a list type
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world';
}
},
human : {
type : humanType,
resolve : () => getHuman()
},
humans : {
type : new GraphQLList(humanType),
resolve : () => getHumans()
}
}
})
});
var query = '{ humans { name, description } }';
graphql(schema, query).then(result => {
// prints { data : { “humans” : [
{ id: 1, name : ‘Luke’, description: ‘anakins son’ },
{ id: 2, name : ‘Vader’, description: ‘anakins?’},
{ id: 3, name : ‘Palpatine’, description: ‘anakins boss’ }] }
console.log(result);
});
Do the query
function getHumans() {
return Promise.resolve(
[{
id: 1, name : ‘Luke’, description: ‘anakins son’
},
{
id: 2, name : ‘Vader’, description: ‘anakins?’
},
{
id: 3, name : ‘Palpatine’, description: ‘anakins boss’
}]
)
}
Resolve dependencies part I
Imagine the following schema
humans : {
type: new GraphQLList(humanType),
resolve : (root, {}) => {
return getHumans();
}
}
getHumans() {
const humans = [
{id :'1', name: 'Luke', description: 'Anakins son', friends : ['2'] },
{id :'2', name: 'Darth', description: 'Anakin', friends : ['3'] },
{id :'3', name: 'Palpatine', description: 'Anakins master', friends : [] }
];
return Promise.resolve( humans );
}
How to resolve these
ids into objects?
var humanType = new GraphQLObjectType({
name : 'Human',
fields : () => ({
id: { type: GraphQLString },
description : { type : GraphQLString, description : 'desc' },
name : { type : GraphQLString, description : 'name of person' },
}),
interfaces : [ characterInterface ]
})
Resolve dependencies part II
Resolve
function getFriends(character) {
return character.friends.map( f => getHuman(f) );
}
Lookup by id
friends : {
type: new GraphQLList(humanType),
resolve : human => getFriends( human )
}
Query with an argument
human : {
type : humanType,
args : {
id: {
description: 'id of the jedi',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }) => getHumanById(id)
}
resolve, we dig out the id
and use it to query our resolver
var query = '{ human(id: 1) { name, description } }';
graphql(schema, query).then(result => {
// prints { data : { “human( id: 1 )” : { “name” : “john”, “description” : “a description” } } }
console.log(result);
});
Do the query
Resolver
Define args
function getHumanById(id) {
let result = return Promise.resolve(
[{
id: 1, name : ‘Luke’, description: ‘anakins son’
},
{
id: 2, name : ‘Vader’, description: ‘anakins?’
},
{
id: 3, name : ‘Palpatine’, description: ‘anakins boss’
}].filter( h => h.id === id );
return result.length === 0 ? null : result[0];
)
}
Aliases
Alias = rename field name in the query
query Test {
github {
userchris: user(username:"softchris") {
repos {
name
}
}
}
}
New value
Old value
“data” : {
“github” : {
“userchris” : {
“repos” : [{ “name” : “…”, … }]
}
}
}
Fragments
Fragment is about cleaning up the code and make part of it
reusable
fragment UserDetail on GithubUser {
company: my_company,
avatar_url,
repos {
name,
commits {
message
}
}
}
query Github($user: String!, $repo: Boolean!) {
github {
user(username :$user) {
...UserDetail
}
}
}
We don’t need
to clutter the query
Update in one place when a field needs to be added
Directives
Directive - Conditionally include / exclude
query ConditionalQuery($var: Boolean) {
otherField,
skipFieldPossibly @skip(if: $var)
}
Skip
query Github($user: String!, $repo: Boolean!) {
github {
user(username :$user) {
company,
avatar_url,
repos @include (if: $repo) {
name,
commits {
message
}
}
}
Include
Mutators
Mutator - delete
function deleteArticle(id) {
return articleService.delete( id )
}
Resolver
export default new GraphQLSchema({
query: QueryType,
mutation: MutationType
});
Define in schema
mutation “Delete Article” {
deleteArticle(id:123) {
status
}
} Response back
Input argument
Call
var MutationType = new GraphQLObjectType({
name: ‘Deletetion Example',
description: ‘removing article',
fields: () => ({
deleteArticle: {
type: ArticleType,
description: 'Delete an article with id and return the article that was deleted.',
args: {
id: { type: new GraphQLNonNull(GraphQLInt) }
},
resolve: (value, { id }) => {
return deleteArticle(id);
}
}
}),
});
Mutator - create
var MutationType = new GraphQLObjectType({
name: 'GraphQL Mutations',
description: 'Mutations',
fields: () => ({
createArticle: {
type: ArticleType,
description: 'Create a new article',
args: {
article: { type: ArticleInputType }
},
resolve: (value, { article }) => {
return createArticle(article);
}
}
}),
});
function createArticle(article) {
return articleService.create( article )
}
Resolver
mutation “Create Article” {
createArticle(article: {
title : “Tomato”,
description : “Veggie”
}) {
status
}
Call
We have learned
Set up a schema
To define different types
Learned about resolvers
Mutators
Directives
Fragments
Aliases
What about consuming the
GraphQL?
{
"data": {
"humans": [
{ "name": “Luke", "description": "Anakins son”, "friends": [{ "name": “Darth" }],},
{ "name": “Darth", "description": “Anakin", "friends": [ { "name": “Palpatine" }],},
{ "name": "Palpatine","description": "Anakins master","friends": [],}
],
}
}
Content-Type: application/graphql
http://localhost:4000/graphql?
query={humans{name,description,friends{name}}}
GET
Apollo client
Inrementally adoptable
Universally compatible
Simple to get started with
Inspectable and understandable
Built for interactive apps
Small and flexible
Community driven
GraphQL Client with integrations to all major frameworks
Also get Chrome plugin
https://chrome.google.com/webstore/detail/apollo-client-
developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm/related
npm install apollo-client graphql-tag --save
Install
Playgrounds
http://graphql.org/swapi-graphql/
Star wars API wrapped in GraphQL
https://www.graphqlhub.com/
Social media such as Twitter, Github, Reddit etc.
Links
graphql-express
Learn to build your own API
https://github.com/softchris/graphql-express-demo
Or cheat and check out my repo :)
http://graphql.org/learn/
Official documentation
Summing up
Is GraphQL replacing REST? You can use both actually
REST has many endpoints
and HATEOAS
GraphQL has one endpoint
and content negotiation
REST is an architecture
concept
GraphQL is query
language + tools
GraphQL only asks for fields
it need
REST responds with a
whole object
Thank you
1 of 48

More Related Content

What's hot(20)

Typescript barcelonaTypescript barcelona
Typescript barcelona
Christoffer Noring660 views
Hidden Docs in AngularHidden Docs in Angular
Hidden Docs in Angular
Yadong Xie787 views
Redux vs AltRedux vs Alt
Redux vs Alt
Uldis Sturms4.4K views
SOLID PrinciplesSOLID Principles
SOLID Principles
Chris Weldon1.5K views
JavaScriptJavaScript
JavaScript
Sunil OS519K views
GWT integration with VaadinGWT integration with Vaadin
GWT integration with Vaadin
Peter Lehto1.4K views
Gwt and XtendGwt and Xtend
Gwt and Xtend
Sven Efftinge2K views
SOLID PRINCIPLESSOLID PRINCIPLES
SOLID PRINCIPLES
Luciano Queiroz868 views
Oleksandr TolstykhOleksandr Tolstykh
Oleksandr Tolstykh
CodeFest412 views
Why rubyWhy ruby
Why ruby
rstankov604 views
Mastering Oracle ADF BindingsMastering Oracle ADF Bindings
Mastering Oracle ADF Bindings
Euegene Fedorenko2.5K views
Ruby/RailsRuby/Rails
Ruby/Rails
rstankov860 views
Backbone jsBackbone js
Backbone js
rstankov1.4K views

More from Christoffer Noring(20)

Azure signalRAzure signalR
Azure signalR
Christoffer Noring129 views
Game dev 101 part 3Game dev 101 part 3
Game dev 101 part 3
Christoffer Noring70 views
Game dev 101   part 2Game dev 101   part 2
Game dev 101 part 2
Christoffer Noring70 views
Game dev workshopGame dev workshop
Game dev workshop
Christoffer Noring65 views
Deploying your static web app to the CloudDeploying your static web app to the Cloud
Deploying your static web app to the Cloud
Christoffer Noring260 views
IaaS with ARM templates for AzureIaaS with ARM templates for Azure
IaaS with ARM templates for Azure
Christoffer Noring244 views
Learning SvelteLearning Svelte
Learning Svelte
Christoffer Noring580 views
Ng spainNg spain
Ng spain
Christoffer Noring264 views
Angular SchematicsAngular Schematics
Angular Schematics
Christoffer Noring1.5K views
Design thinkingDesign thinking
Design thinking
Christoffer Noring860 views
Keynote ijsKeynote ijs
Keynote ijs
Christoffer Noring438 views
Vue fundamentasl with Testing and VuexVue fundamentasl with Testing and Vuex
Vue fundamentasl with Testing and Vuex
Christoffer Noring626 views
Ngrx slidesNgrx slides
Ngrx slides
Christoffer Noring2.8K views
KendouiKendoui
Kendoui
Christoffer Noring1.2K views
Rxjs viennaRxjs vienna
Rxjs vienna
Christoffer Noring735 views
Rxjs marble-testingRxjs marble-testing
Rxjs marble-testing
Christoffer Noring1.3K views
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
Christoffer Noring1.1K views
Rxjs swetuggRxjs swetugg
Rxjs swetugg
Christoffer Noring536 views
Angular modules in depthAngular modules in depth
Angular modules in depth
Christoffer Noring2.3K views
Finjs - Angular 2 better faster strongerFinjs - Angular 2 better faster stronger
Finjs - Angular 2 better faster stronger
Christoffer Noring520 views

Recently uploaded(20)

Green Leaf Consulting: Capabilities DeckGreen Leaf Consulting: Capabilities Deck
Green Leaf Consulting: Capabilities Deck
GreenLeafConsulting147 views
Liqid: Composable CXL PreviewLiqid: Composable CXL Preview
Liqid: Composable CXL Preview
CXL Forum114 views
METHOD AND SYSTEM FOR PREDICTING OPTIMAL LOAD FOR WHICH THE YIELD IS MAXIMUM ...METHOD AND SYSTEM FOR PREDICTING OPTIMAL LOAD FOR WHICH THE YIELD IS MAXIMUM ...
METHOD AND SYSTEM FOR PREDICTING OPTIMAL LOAD FOR WHICH THE YIELD IS MAXIMUM ...
Prity Khastgir IPR Strategic India Patent Attorney Amplify Innovation22 views
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
Eleanor McHugh31 views

Graphql, REST and Apollo

  • 1. GraphQL vs REST Chris Noring Google Developer Expert Digital McKinsey @chris _noring
  • 2. Why should I care Presentation Structure How to wrap an API in GraphQL REST GraphQL comparison
  • 3. Why should I care?
  • 5. GET /user POST /user PUT /user DELETE /user GET /user/query.. Products New resources needs to be added Orders Categories … Number of endpoints becomes really large, really fast
  • 6. Resources needs to look different if we are dealing with mobile or desktop GET /users/1 data : { firstname : ‘chris’, } GET /mobile/users/1 data : { firstname : ‘chris’, region : { url : ‘/regions/1’ } } Only the linkEagerly loaded region : { id : 1, city : ‘Warsaw’, country : ‘Poland’ }
  • 7. Problem 2 Need for a unified way of asking for data Twitter Reddit Github Data Service GraphQL runtime Frontend Query
  • 8. Problem 3 Not the output I want
  • 9. Mismatch in response Mapping is needed { “data” : { “name”, “address” : “Warsaw” } } class Person { surname: string; city: string, } Need to rename to avoid remapping in Frontend
  • 10. Might need several endpoints class Person { fname : string; surname: string; address: string, friends : [], records : [] } Might lead to multiple requests api/people/1 api/people/1/friends api/people/1/records
  • 12. GraphQL Lets the client specify what they need, “content negotiation” Easier to aggregate data from many sources Uses a type system to describe data Application layer query language Multiple resources in a single request Open sourced by Facebook in 2015
  • 16. { resource { column, column2 } } Query data : { resource { column, column2 } } Response
  • 17. Query example Given these resources User ( id, name, age, address, orders) Order ( id, created, items ) OrderItem ( id, product, Quantity, Price ) Product ( id, Name ) Users { name, orders { created, items { Quantity, Price, product { Name } } } } data : [{ ‘John’, orders : [{ ‘2017-01-01’, items : [{ Quantity : 3, Price : 110, Product : { Name : ‘DVD’ } }] }] }]
  • 18. Query with parameter Users( email : ‘chris@chris.com’ ) { … } SELECT … FROM resource WHERE email =‘chris@chris.com’
  • 19. Query with operators Users( email__contains : ‘chris’ ) { } is, not, lt, lte, gt, gte, contains, icontains, startswith, endswith, iendswith, like, ilike
  • 21. Define a schema Resolving a query Building a runtime is about the following Define queries Define mutators Create Update Delete
  • 22. Runtime: hello world import { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; var schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', fields: { hello: { type: GraphQLString, resolve() { return 'world'; } } } }) }); Define a schema Define a query Define a queryable field Respond/Resolve what to answer var query = '{ hello }'; graphql(schema, query).then(result => { // prints { data : { “hello” : “world” } } console.log(result); }); Do the query
  • 23. Where to go from here?
  • 24. A more advanced schema var schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', fields: { hello: { type: GraphQLString, resolve() { return 'world'; } }, human : { type : humanType, resolve : () => getHuman() } } }) }); function getHuman() { return Promise.resolve( return { id : ‘1’, description : ‘ a description’, name : ‘john’ }) } Our resolver should call a db ideally Resolver function var humanType = new GraphQLObjectType({ name : 'Human', fields : () => ({ id: { type: GraphQLString }, description : { type : GraphQLString }, name : { type : GraphQLString } }), }) } Complex type var query = '{ human { name, description } }'; graphql(schema, query).then(result => { // prints { data : { “human” : { “name” : “john”, “description” : “a description” } } } console.log(result); }); Do the query
  • 25. Define and query a list type var schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', fields: { hello: { type: GraphQLString, resolve() { return 'world'; } }, human : { type : humanType, resolve : () => getHuman() }, humans : { type : new GraphQLList(humanType), resolve : () => getHumans() } } }) }); var query = '{ humans { name, description } }'; graphql(schema, query).then(result => { // prints { data : { “humans” : [ { id: 1, name : ‘Luke’, description: ‘anakins son’ }, { id: 2, name : ‘Vader’, description: ‘anakins?’}, { id: 3, name : ‘Palpatine’, description: ‘anakins boss’ }] } console.log(result); }); Do the query function getHumans() { return Promise.resolve( [{ id: 1, name : ‘Luke’, description: ‘anakins son’ }, { id: 2, name : ‘Vader’, description: ‘anakins?’ }, { id: 3, name : ‘Palpatine’, description: ‘anakins boss’ }] ) }
  • 26. Resolve dependencies part I Imagine the following schema humans : { type: new GraphQLList(humanType), resolve : (root, {}) => { return getHumans(); } } getHumans() { const humans = [ {id :'1', name: 'Luke', description: 'Anakins son', friends : ['2'] }, {id :'2', name: 'Darth', description: 'Anakin', friends : ['3'] }, {id :'3', name: 'Palpatine', description: 'Anakins master', friends : [] } ]; return Promise.resolve( humans ); } How to resolve these ids into objects?
  • 27. var humanType = new GraphQLObjectType({ name : 'Human', fields : () => ({ id: { type: GraphQLString }, description : { type : GraphQLString, description : 'desc' }, name : { type : GraphQLString, description : 'name of person' }, }), interfaces : [ characterInterface ] }) Resolve dependencies part II Resolve function getFriends(character) { return character.friends.map( f => getHuman(f) ); } Lookup by id friends : { type: new GraphQLList(humanType), resolve : human => getFriends( human ) }
  • 28. Query with an argument human : { type : humanType, args : { id: { description: 'id of the jedi', type: new GraphQLNonNull(GraphQLString) } }, resolve: (root, { id }) => getHumanById(id) } resolve, we dig out the id and use it to query our resolver var query = '{ human(id: 1) { name, description } }'; graphql(schema, query).then(result => { // prints { data : { “human( id: 1 )” : { “name” : “john”, “description” : “a description” } } } console.log(result); }); Do the query Resolver Define args function getHumanById(id) { let result = return Promise.resolve( [{ id: 1, name : ‘Luke’, description: ‘anakins son’ }, { id: 2, name : ‘Vader’, description: ‘anakins?’ }, { id: 3, name : ‘Palpatine’, description: ‘anakins boss’ }].filter( h => h.id === id ); return result.length === 0 ? null : result[0]; ) }
  • 30. Alias = rename field name in the query query Test { github { userchris: user(username:"softchris") { repos { name } } } } New value Old value “data” : { “github” : { “userchris” : { “repos” : [{ “name” : “…”, … }] } } }
  • 32. Fragment is about cleaning up the code and make part of it reusable fragment UserDetail on GithubUser { company: my_company, avatar_url, repos { name, commits { message } } } query Github($user: String!, $repo: Boolean!) { github { user(username :$user) { ...UserDetail } } } We don’t need to clutter the query Update in one place when a field needs to be added
  • 34. Directive - Conditionally include / exclude query ConditionalQuery($var: Boolean) { otherField, skipFieldPossibly @skip(if: $var) } Skip query Github($user: String!, $repo: Boolean!) { github { user(username :$user) { company, avatar_url, repos @include (if: $repo) { name, commits { message } } } Include
  • 36. Mutator - delete function deleteArticle(id) { return articleService.delete( id ) } Resolver export default new GraphQLSchema({ query: QueryType, mutation: MutationType }); Define in schema mutation “Delete Article” { deleteArticle(id:123) { status } } Response back Input argument Call var MutationType = new GraphQLObjectType({ name: ‘Deletetion Example', description: ‘removing article', fields: () => ({ deleteArticle: { type: ArticleType, description: 'Delete an article with id and return the article that was deleted.', args: { id: { type: new GraphQLNonNull(GraphQLInt) } }, resolve: (value, { id }) => { return deleteArticle(id); } } }), });
  • 37. Mutator - create var MutationType = new GraphQLObjectType({ name: 'GraphQL Mutations', description: 'Mutations', fields: () => ({ createArticle: { type: ArticleType, description: 'Create a new article', args: { article: { type: ArticleInputType } }, resolve: (value, { article }) => { return createArticle(article); } } }), }); function createArticle(article) { return articleService.create( article ) } Resolver mutation “Create Article” { createArticle(article: { title : “Tomato”, description : “Veggie” }) { status } Call
  • 38. We have learned Set up a schema To define different types Learned about resolvers Mutators Directives Fragments Aliases
  • 39. What about consuming the GraphQL?
  • 40. { "data": { "humans": [ { "name": “Luke", "description": "Anakins son”, "friends": [{ "name": “Darth" }],}, { "name": “Darth", "description": “Anakin", "friends": [ { "name": “Palpatine" }],}, { "name": "Palpatine","description": "Anakins master","friends": [],} ], } } Content-Type: application/graphql http://localhost:4000/graphql? query={humans{name,description,friends{name}}} GET
  • 42. Inrementally adoptable Universally compatible Simple to get started with Inspectable and understandable Built for interactive apps Small and flexible Community driven GraphQL Client with integrations to all major frameworks
  • 43. Also get Chrome plugin https://chrome.google.com/webstore/detail/apollo-client- developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm/related npm install apollo-client graphql-tag --save Install
  • 45. http://graphql.org/swapi-graphql/ Star wars API wrapped in GraphQL https://www.graphqlhub.com/ Social media such as Twitter, Github, Reddit etc.
  • 46. Links graphql-express Learn to build your own API https://github.com/softchris/graphql-express-demo Or cheat and check out my repo :) http://graphql.org/learn/ Official documentation
  • 47. Summing up Is GraphQL replacing REST? You can use both actually REST has many endpoints and HATEOAS GraphQL has one endpoint and content negotiation REST is an architecture concept GraphQL is query language + tools GraphQL only asks for fields it need REST responds with a whole object