9th Nov 2016
Berlin
some sea
some sea
some sea
some border
some border
some border
some border
some city
some city
some city
some city
some city
some city
some city
some city
I live here
you live here
kamilkisiela
app is based on components
app = component
components need data
@Component({
template: `
Author: {{comment.author}}
Text: {{comment.text}}
`
})
class CommentComponent {
comment: Comment;
}
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);
}
}
where is the problem?
managing data is not ideal now
Smart
Dumb
Container
Presentational
what it means "smart and dumb"?
data
provide
display
Smart component
Dumb component
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);
}
}
make it dumb
@Component({
template: `
Author: {{comment?.author}}
Text: {{comment?.text}}
`
})
class CommentComponent {
@Input() comment: Comment;
}
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())
}
}
yes, services, we love them!
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);
}
}
now everyone is happy
and your developers!
http
http
comment #1
comment #3
comment #4
comment #2
time
comment
comment
{
id: 13
likes: 20
}
{
id: 13
likes: 21
}
someone likes the comment
size does matter
GraphQL is awesome!
posted by Kamil
{ text: "GraphQL is awesome!", postedBy: "Kamil", createdAt: 1478368227, likes: 2 }
data structure doesn't always fit the actual needs
{
contents: "GraphQL is awesome!",
author: "Kamil"
}
{{comment.text}} by {{comment.postedBy}}
API change requires a reflection in an app
thank you
"oh no... he continues"
they say GraphQL is a cure for everything!
but... it doesn't solve every of those problems
GraphQL makes client totally independent
client depends on API
and change it any time you want in any component
overfetched data
define exact shape of data you want to use
adjustments
bound it with components
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
});
GraphQL is just a language
let's find proper solutions
@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);
}
}
where we left off
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());
}
}
how it looks now
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)
}
}
with GraphQL
and use only one
unnecessary requests
wait for some time to catch the same requests
time
10 ms
batching
server
with batching we don't get different results
for the same queries
inconsistent data
use store to keep all the results
overloaded network
use store to cache a query
cache
server
request
UI
our service is getting more and more complex
looks like it could be helpful for many people
somewhere there is a magic tool!
solves the problems we talked about
to create modern web apps
Apollo to the rescue
users don't like to wait
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
1.
2.
2.
3.
how it works
Mutation
UI
optimistic response
real response
how to make an app even faster?
without
1. Enter the route
2. Request is made
3. Wait for the response
4. Update the UI
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
get the experience
keep things updated
action
server
clients
pub sub
people should visit!
click the logo to see a working example
prefetch queries on the server
server
client
fetch query
save to store
fetch query
render with the same store
update the UI imidiately
make it even faster
compilation to speed up the runtime
we could make just one request with all queries
one request per render
You can even specify your custom batch interval
time
10 ms
batching
server
interval
can I use it with other frameworks?
all share the same principals
hit me with any questions!
it was fun!
feel free to message me
come to Poland!
*danke
bye bye bye bye bye bye