Workshop

17/November/2016

Poznan, Poland

Hi!

I'm Kamil

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

+

Build modern web apps with GraphQL

 

GraphQL

What?

What is GraphQL?

What?

-   data query language

-   invented and used by Facebook in 2012

-   open-sourced in 2015

-   response to modern web app needs

Why?

Why Facebook invented GraphQL?

Why?

-  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

Where?

Where is a place for GraphQL?

GraphQL

Mobile APP

Web APP

ApolloStack

Relay

Microservices

Databases

External APIs

Client

Server

How?

How does GraphQL look like?

{
  me {
    name
  }
}

How?

Like this

Solves

What does GraphQL solve?

  • API Documentation
  • Multiple roundtrips to the client
  • Overfetched data

We don't use these!

!

!

!

!

!

!

Future

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

Who is using GraphQL?

and many many other

 

Query

a way to fetch data

Query

-  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

query getMe {
    me {
        name
    }
}

Query

{
    "loading": false,
    
    "data": {
        "me": {
            "name": "Kamil"
        }
    }
}

Result

In Apollo

Result, a bit closer

Loading

ApolloQueryResult

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"
        }
    }
}

Angular2Apollo

apollo.watchQuery

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
    }
}

apollo.query

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
    }
}

 

Mutation

a way to manipulate data

POST in GraphQL

Mutation

Mutation

Result

mutation add($content: String!) {
  addComment(content: $content) {
    content
    createdAt
  }
}
{
    "addComment": {
        "content": "Love it!",
        "createdAt": 1232434234
    }
}

In Apollo

Mutation

Result

mutation add($content: String!) {
    addComment (content: $content) {
        content
        createdAt
    }
}
{
    "data": {
        "addComment": {
            "content": "Love it!",
            "createdAt": 1232434234
        }
    }
}

Angular2Apollo

apollo.mutate

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
    }
}

 

Server

Server

Combination of two things

GraphQL endpoint

Schema

Resolvers

Execute

Server

Schema

{
    type User {
        name: String!
        nickname: String
    }

    type Query {
        users: [User]
    }

    type Mutation {}
    type Subscription {}
    
    schema: {
        query: Query
        mutation: Mutation
        subscription: Subscription
    }
}

Server

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
    }
}

Resolvers

(root, args, context) => {}

Resolvers

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

Resolvers

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
            });

        }
    }
}

Resolvers

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

 

Features

Self-documenting

Introspection

{
  __schema {
    types {
      name
    }
  }
}

Introspection

gives

{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Query"
        },
        {
          "name": "User"
        },
        {
          "name": "String"
        },
        {
          "name": "Entry"
        }
      ]
    }
  }
}

Ask a GraphQL schema for information about what queries it supports

Introspection

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

Introspection

GraphiQL gets the information using Introspection system

Query Batching

1 request

1 req

1 req

1 req

1 req

1 req

1 req

= 8* round trips

* one more to fetch all the photo ids

Only 1 request with Query Batching

Query Batching

- GraphQL query call

Time (ms)

< 10 ms

> 10 ms

- actual request

You can even specify your custom batch interval

Optimistic UI

1. Mutation is called

2. Optimistic Response

3. Server response

Optimistic UI

An example

Optimistic UI

Mutation

Server

UI

Optimistic response

Not so optimistic response

Hits UI first

Replaces the optimistic response if different

Prefetching

Prefetching

Without

Prefetching

With

Prefetching

Uses cache to avoid hitting server

Query

Cache

Server

UI

Subscriptions

PubSub system

Client

Client

Subscribe

Action

Mutation

GraphQL

Responses

Subscriptions

Server Side Rendering

Click the logo to see a working example

Ahead of Time

compilation

AngularJS Client

Click the logo

Migration

Migration

Both side by side

REST

GraphQL Server

REST

Client - receiver

 

Resources

Resources

GraphQL.org

dev.ApolloData.com

learnGraphQL.com

Questions?

?

Thank you!

It was fun!

Feel free to message me!

Come to Warsaw!

bye bye bye bye bye bye bye

GraphQL + Angular | ng-poznan workshop

By Kamil Kisiela

GraphQL + Angular | ng-poznan workshop

  • 858