17/November/2016
Poznan, Poland
kamilkisiela
Poznan
Warsaw
Some Sea
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some City
Some Sea
Some Sea
Some Sea
- data query language
- invented and used by Facebook in 2012
- open-sourced in 2015
- response to modern web app needs
- Introspection!
- Invented during the move from HTML5 to native
- Big NO to REST
- multiple round trips may overload the network (mobile)
- client depends on server
- GraphQL is strongly-typed
- GraphQL is self-documented
GraphQL
Mobile APP
Web APP
ApolloStack
Relay
Microservices
Databases
External APIs
Client
Server
{ me { name } }
Like this
What does GraphQL solve?
We don't use these!
!
!
!
!
!
!
What will GraphQL try to solve? *
- Real Time data updates
- Access the most important data first
* or won't, depends if Facebook need those internally
Who is using GraphQL?
and many many other
- declare data needs as you write a query
- models data the same way we think about the data in real life
- designed it to be fast and efficient
- result looks the same as the query
- allows components to fetch their own data, exactly what they need
{
me {
name
}
}
Query
{
"me": {
"name": "Kamil"
}
}
Result
query getMe {
me {
name
}
}
Query
{
"loading": false,
"data": {
"me": {
"name": "Kamil"
}
}
}
Result
Loading
import { ApolloQueryResult } from 'apollo-client';
{
"loading": false,
}
Boolean that tells if the result is completed or still loading
Data
The actual result of a query
{
"data": {
"me": {
"name": "Kamil"
}
}
}
import { Angualr2Apollo } from 'angular2-apollo';
@Component({ ... })
class MeComponent implements OnInit {
me: Object;
loading: boolean = true;
constructor(
private apollo: Angular2Apollo
) {}
ngOnInit() {
this.apollo
.watchQuery({ query })
.subscribe((result) => {
this.me = result.data.me;
this.loading = result.loading;
});
}
}
query getMe {
me {
name
}
}
import { Angualr2Apollo } from 'angular2-apollo';
@Component({ ... })
class MeComponent implements OnInit {
me: Object;
loading: boolean = true;
constructor(
private apollo: Angular2Apollo
) {}
ngOnInit() {
this.apollo
.query({ query })
.then((result) => {
this.me = result.data.me;
this.loading = result.loading;
});
}
}
query getMe {
me {
name
}
}
POST in GraphQL
Mutation
Result
mutation add($content: String!) {
addComment(content: $content) {
content
createdAt
}
}
{
"addComment": {
"content": "Love it!",
"createdAt": 1232434234
}
}
Mutation
Result
mutation add($content: String!) {
addComment (content: $content) {
content
createdAt
}
}
{
"data": {
"addComment": {
"content": "Love it!",
"createdAt": 1232434234
}
}
}
import { Angualr2Apollo } from 'angular2-apollo';
@Component({ ... })
class NewCommentComponent {
constructor(
private apollo: Angular2Apollo
) {}
submit(comment: string) {
this.apollo
.mutate({
query,
variables: {
content: comment
}
})
.then((result) => {
const newComment = result.data.addComment;
console.log('Added:' newComment.content);
console.log('At:' newComment.createdAt);
});
}
}
mutation add($content: String!) {
addComment (content: $content) {
content
createdAt
}
}
Combination of two things
GraphQL endpoint
Schema
Resolvers
Execute
Schema
{
type User {
name: String!
nickname: String
}
type Query {
users: [User]
}
type Mutation {}
type Subscription {}
schema: {
query: Query
mutation: Mutation
subscription: Subscription
}
}
Resolvers
{
type User {
name: String!
nickname: String
}
type Query {
users: [User]
}
schema: {
query: Query
}
}
{
Query: {
users: () => Users.getAll()
},
User: {
name: (root) => root.name,
nickname: (root) => root.nick_name
}
}
root - contains a previous result
Query
users: [User]
User
name
Resolves with an array of users
user 1
user 2
user n
user 1
user 2
user n
nickname
{
name: ...
nick_name: ...
}
root.name
root.nick_name
User
name
nickname
User
name
nickname
args - contains the arguments of a query
Resolves with an array of first n users
{
type Query {
users(first: Int!): [User]
}
}
{
Query: {
users(root, args) {
return Users.getAll({
limit: args.first
});
}
}
}
context - globally available objects
{
Query: {
users(root, args, context) {
return context.users.getAll();
}
}
}
You can define an object that will be
accessible in every resolver function
import Users from './models/users';
import schema from './schema';
import resolvers from './resolvers';
const graphqlEndpoint = {
schema,
resolvers,
context: {
users: new Users()
}
};
export default graphqlEndpoint
{
__schema {
types {
name
}
}
}
gives
{
"data": {
"__schema": {
"types": [
{
"name": "Query"
},
{
"name": "User"
},
{
"name": "String"
},
{
"name": "Entry"
}
]
}
}
}
Ask a GraphQL schema for information about what queries it supports
type Repository {
name: String!
owner: String!
description: String
url: String!
stars: Int!
issues: Int
}
GraphiQL gets the information using Introspection system
1 request
1 req
1 req
1 req
1 req
1 req
1 req
* one more to fetch all the photo ids
- GraphQL query call
Time (ms)
< 10 ms
> 10 ms
- actual request
You can even specify your custom batch interval
1. Mutation is called
2. Optimistic Response
3. Server response
An example
Mutation
Server
UI
Optimistic response
Not so optimistic response
Hits UI first
Replaces the optimistic response if different
Without
With
Uses cache to avoid hitting server
Query
Cache
Server
UI
PubSub system
Client
Client
Subscribe
Action
Mutation
GraphQL
Responses
Click the logo to see a working example
compilation
Click the logo
Both side by side
REST
GraphQL Server
REST
Client - receiver
GraphQL.org
dev.ApolloData.com
learnGraphQL.com
?
It was fun!
Feel free to message me!
Come to Warsaw!
bye bye bye bye bye bye bye