16th Mar 2017
Poznan
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
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
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
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
apps are getting more and more advanced
@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
We make a
Http request
with GraphQL
Data we want to fetch
a comment
an author
with GraphQL
{ Comment(id: 12) { text postedBy } }
Data we want to fetch
a comment
an author
Equivalent query
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
We send a query using Http request
how to solve those issues we talked about?
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
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
how it works
An action
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
Thanks to introspection
documentation writes itself
type Repository { name: String! owner: String! description: String url: String! stars: Int! issues: Int }
Validation, autocompiletion and more
migration
side by side - hide REST behind GraphQL
client - receiver
to create a missing integration, for example
hit me with any question!
it was fun!
feel free to message me
*dziękuję
bye bye bye bye bye bye