16th Mar 2017

Poznan

GraphQL

a missing piece of modern web apps


3


2


1

Cześć

or hello?

kamilkisiela

GraphQL and Angular

Landing on the ngMoon with Apollo

GraphQL and any framework

Landing on the everyMoon with Apollo


today

components

components

app is based on components

app

components

app = component

app

data

components need data




    @Component({
      template: `
        Author: {{comment.author}}
        Text: {{comment.text}}
      `
    })
    class CommentComponent {
      comment: Comment;
    }

data

provide it directly




    @Component({
      template: `
        Author: {{comment?.author}}
        Text: {{comment?.text}}
      `
    })
    class CommentComponent {
      comment: Comment;
      @Input() commentId: number;

      constructor(private http: Http) {}

      ngOnInit() {

        this.http.get(`https://api.com/comment/${this.commentId}`)
          .map(res => res.json())
          .subscribe((comment) => this.comment = comment);

      }
    }

problem?

where is the problem?

with data

managing data is not ideal now

there must be a better way!

components

Smart

Dumb

Container

Presentational

=

components

what it means "smart and dumb"?

data

provide

display

Smart component

Dumb component

back to the example

data

data is provided directly




    @Component({
      template: `
        Author: {{comment?.author}}
        Text: {{comment?.text}}
      `
    })
    class CommentComponent {
      comment: Comment;
      @Input() commentId: number;

      constructor(private http: Http) {}

      ngOnInit() {

        this.http.get(`https://api.com/comment/${this.commentId}`)
          .map(res => res.json())
          .subscribe((comment) => this.comment = comment);

      }
    }

data

make it dumb




    @Component({
      template: `
        Author: {{comment?.author}}
        Text: {{comment?.text}}
      `
    })
    class CommentComponent {
      @Input() comment: Comment;
    }

data

add the brain




    @Component({
      template: `
        <comment [comment]="comment | async"></comment>
      `
    })
    class CommentContainerComponent {
      @Input() commentId: number;
      comment: Observable<Comment>;

      constructor(private http: Http) {}

      ngOnInit() {

        this.comment = this.http
          .get(`https://api.com/comment/${this.commentId}`)
          .map(res => res.json())

      }
    }

still not a good idea

services!

yes, services, we love them!

service

use a service to handle data fetching




    @Component({
      template: `
        <comment [comment]="comment | async"></comment>
      `
    })
    class CommentContainerComponent {
      @Input() commentId: number;
      comment: Observable<Comment>;

      constructor(private commentsApi: CommentsApi) {}

      ngOnInit() {

        this.comment = this.commentsApi.single(this.commentId);

      }
    }

pff... done

now everyone is happy

except your clients...

and your developers!


problems

overloaded network

overloaded network

API

http

unnecessary requests

unnecessary requests

API

http

comment #1

comment #3

comment #4

comment #2

inconsistent data

inconsistent data

time

comment

comment

{

  id: 13

  likes: 20

}
{

  id: 13

  likes: 21

}

someone likes the comment

overfetched data

overfetched data

size does matter

GraphQL is awesome!

posted by Kamil

{

  text: "GraphQL is awesome!",

  postedBy: "Kamil",

  createdAt: 1478368227,

  likes: 2

}

adjustments

adjustments

data structure doesn't always fit the actual needs

{

  contents: "GraphQL is awesome!",

  author:   "Kamil"

}
{{comment.text}} 
by
{{comment.postedBy}}

client depends on API

API change requires a reflection in an app


solutions

GraphQL!

gra... what?!

What is GraphQL?

data query language

invented and used by Facebook in 2012

open-sourced in 2015

response to modern web app needs

(we will talk about it soon)

GraphQL

microservices

databases

external APIs

mobile

web

desktop

server

client

connects both worlds

GraphQL

{
  me {
    name
  }
}

Hello World

How does it work?

GraphQL

type Project {
  name: String
  tagline: String
  contributors: [User]
}

Describe your data on the server

server

GraphQL

type Project {
  name: String
  tagline: String
  contributors: [User]
}
{
  project(name: "GraphQL") {
    tagline
  }
}

Describe your data on the server

Ask for what you want

query

server

GraphQL

client

type Project {
  name: String
  tagline: String
  contributors: [User]
}
{
  project(name: "GraphQL") {
    tagline
  }
}

Describe your data on the server

Ask for what you want

Get predictable results

{
  "project": {
    "tagline": "A query language for APIs"
  }
}

query

server

yet again

GraphQL

client

server

db

GraphQL

client

server

{
  project(name: "GraphQL") {
    tagline
  }
}

db

GraphQL

client

server

{
  project(name: "GraphQL") {
    tagline
  }
}

db

type Project {
  name: String
  tagline: String
  contributors: [User]
}

GraphQL

client

server

{
  project(name: "GraphQL") {
    tagline
  }
}
{
  "project": {
    "tagline": "A query language for APIs"
  }
}

db

GraphQL users

and many others

But... why?

apps are getting more and more advanced

back to the example




    @Component({
      template: `
        <comment [comment]="comment | async"></comment>
      `
    })
    class CommentContainerComponent {
      @Input() commentId: number;
      comment: Observable<Comment>;

      constructor(private commentsApi: CommentsApi) {}

      ngOnInit() {

        this.comment = this.commentsApi.single(this.commentId);

      }
    }

back to the example

where we left off

CommentsApi

our service to fetch comments from the API




    class CommentsApi {
      url: string = `https://api.com/comment/`;
 
      constructor(private http: Http) {}
 
      single(id: number) {

        return this.http.get(this.url + id)
          .map(res => res.json());

      }
    }

CommentsApi

how it looks now

We make a

Http request

implement GraphQL

CommentsApi

with GraphQL

Data we want to fetch

a comment

an author

CommentsApi

with GraphQL

{
  Comment(id: 12) {
    text
    postedBy
  }
}

Data we want to fetch

a comment

an author

Equivalent query

put it into the service


    class CommentsApi {
      url: string = `https://api.com/query?=`;
 
      constructor(private http: Http) {}
 
      single(id: number) {

        const query = `
          query getComment {
            Comment(id: ${id}) {
              text
              postedBy
            }
          }
        `;

        return this.query(query);

      }

      query(query: string) {

        return this.http.get(this.url + JSON.stringify(query))
          .map(res => res.json().data.comment)

      }
    }

CommentsApi

with GraphQL

We send a query using Http request

back to solutions

how to solve those issues we talked about?

we just did it!

by using GraphQL

THE END

thank you

NOT

"oh no... he continues"

not the end?

they say GraphQL is a cure for everything!

GraphQL is helpful

but... it doesn't solve every of those problems

what we solved

separation

GraphQL makes client totally independent

client depends on API

define what you need

and change it any time you want in any component

overfetched data

own the structure

define exact shape of data you want to use

adjustments

use fragments

bound it with components

use fragments

to define what a component needs


    // Comment component

    CommentInfoFragment = gql`
      fragment CommentInfo on Comment {
        id
        text
        author
      }
    `;

    // CommentsPage Component

    const ALL_COMMENTS_QUERY = gql`
      query getAllComments {
        allComments {
          ...CommentInfo
        }
      }

      ${CommentInfoFragment}
    `;

    this.apollo.watchQuery({
      query: ALL_COMMENTS_QUERY
    });


what about the network?

GraphQL is just a language

catch them all!

and use only one

unnecessary requests

Request batching

wait for some time to catch the same requests

time

10 ms

batching

server

solved for requests

with batching we don't get different results

for the same queries

inconsistent data

caching

use store to keep all the results

overloaded network

caching

use store to cache a query

cache

server

request

UI

bigger and bigger

our service is getting more and more complex

open-source it!

looks like it could be helpful for many people

someone did

somewhere there is a magic tool!

it's called


apollo

what is Apollo?

GraphQL client

solves the problems we talked about

exactly what we need

to create modern web apps

so

we own the network

but...

what about UX?

Apollo to the rescue


UX

latency

users don't like to wait

Optimistic UI

Optimistic UI

don't make them wait

1. We submit a comment

2. Request is made

2. We update the UI in the same moment

3. We get the response from the server later

Optimistic UI

how it works

An action

UI

optimistic response

real response

latency yet again

how to make an app even faster?

prefetching

prefetching

without

1. Enter the route

2. Request is made

3. Wait for the response

4. Update the UI

prefetching

with

1. User puts cursor over the link

2. Request is made to fetch the query

3. We put the response inside the cache

4. User enters the page imidiately

real time

get the experience

Subscriptions

Subscriptions

keep things updated

action

server

clients

pub sub

SEO

people should visit!

server side rendering

click the logo to see a working example

server side rendering

prefetch queries on the server

server

client

fetch query

save to store

fetch query

render with the same store

update the UI imidiately

still not enough

make it even faster

Ahead-of-Time

compilation to speed up the runtime

what if...

we could make just one request with all queries

Query Batching

one request per render

Query batching

You can even specify your custom batch interval

time

10 ms

batching

server

interval

what about us... developers


DX

GraphQL is self-documented

Thanks to introspection

documentation writes itself

type Repository {
  name: String!
  owner: String!
 description: String 
  url: String! 
  stars: Int!
 issues: Int
}

enhanced IDE

Validation, autocompiletion and more

cool


migration
 

migration

side by side - hide REST behind GraphQL

client - receiver


frameworks
 

not only

any framework

help and contribute

to create a missing integration, for example


enough

hit me with any question!

?

it was fun!

feel free to message me


thanks

*dziękuję

bye bye bye bye bye bye

GraphQL and Angular at ng-poznan #28

By Kamil Kisiela

GraphQL and Angular at ng-poznan #28

Let's talk about problems with data management in Angular and why we should use GraphQL and Apollo. Is it a solution for modern web apps that we're all looking for?

  • 689