11th Mar 2017
Warsaw
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 Kariera IT Warsaw
By Kamil Kisiela
GraphQL and Angular at Kariera IT Warsaw
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?
- 637