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