Test Driving Apollo Client 2.0
by Gerard Sans (@gerardsans)
Google Developer Expert
Master of Ceremonies
Blogger
International Speaker
Spoken at 56 events in 18 countries
Angular Trainer
Community Leader
900
1.2K
Angular In Flip Flops
GraphQL Timeline
2012
GraphQL created at Facebook
2015
GraphQL is open sourced
Relay Classic is open sourced
2016
New GraphQL website graphql.org
First GraphQL Summit
GitHub announces GraphQL API
Angular (v2)
2017
Relay Modern 1.0
Apollo Client 2.0
Angular (v5)
Relay vs Apollo Downloads
launchpad.graphql.com
graphql.com
Developer Tools
Todo App
GraphQL Schema
Type System
- Scalar Types: Int, Float, String, Boolean, ID
- Object Types: Todo
- Entry points: Query, Mutation, Subscription
Schema Syntax
- Optional: String, Todo
- Mandatory: String!, Todo!
- Arrays: [String], [Todo]
npm install --global graphcool graphcool init
graphcool push graphcool console
graphcool-cli
Todos Schema
// project.graphcool
type Todo @model {
id: ID! @isUnique
text: String!
complete: Boolean!
}
demo
Apollo Client
2.0
Apollo
Cache
Apollo Client
Apollo
Link
Apollo Cache
Packages
- apollo-cache-inmemory
- apollo-cache-hermes
InMemoryCache
- Normalised and flattened
- apollo.getClient()
- readQuery, writeQuery
- readFragment, writeFragment
Add Todo
const query = gql`query AllTodos {
allTodoes { id text complete }
}`;
const data = store.readQuery<Todos>({ query });
const newTodo = {
id: createTodo.id,
text: input.value,
complete: false,
__typename: "Todo"
};
store.writeQuery({
query,
data: {
allTodoes: [...data.allTodoes, newTodo],
},
});
Apollo Link
Packages
- apollo-link
- apollo-link-http
- apollo-link-ws
- apollo-link-error
- apollo-link-batch
- apollo-link-polling
- apollo-link-retry
Packages (future)
- apollo-link-store
- apollo-link-rest
- apollo-link-offline
Apollo Link
- concat(link1, link2)
- split(expr, link1, link2)
- from([link1, link2])
Security Headers
export class GraphQLModule {
constructor(
private apollo: Apollo,
private httpLink: HttpLink
) {
const http = httpLink.create();
const authMiddleware = new ApolloLink((operation, forward) => {
operation.setContext({
headers: { 'Authorization': localStorage.getItem('token') || '' }
});
return forward(operation);
});
apollo.create({
link: concat(authMiddleware, http)
});
}
}
Using GraphQL
GraphQL Server
source: blog
Dependencies
- JavaScript client for GraphQL (apollo-client)
- API to run queries and mutations (apollo-angular)
- Apollo Link (apollo-link)
- Store (apollo-cache-inmemory)
- Template literal (graphql-tag)
Setup
@NgModule({
imports: [ HttpClientModule, HttpLinkModule ],
exports: [ ApolloModule ]
})
export class GraphQLModule {
constructor( apollo: Apollo, httpLink: HttpLink ) {
apollo.create({
// localhost/graphql (default)
link: httpLink.create({
uri: GRAPHCOOL_URI
}),
cache: new InMemoryCache(),
});
}
}
Bootstrap
// app.module.ts
import { GraphQLModule } from './graphql.module';
@NgModule({
imports: [
BrowserModule,
GraphQLModule,
],
bootstrap: [AppComponent]
})
export class AppModule { }
Todos Schema
schema {
query: Query,
mutation: Mutation
}
type Todo {
id: ID!
text: String!
complete: Boolean!
}
type Query {
allTodoes(skip: Int, take: Int): [Todo!]!
}
Main APIs
- query (QueryRef)
- watchQuery (QueryRef)
- mutate (Observable)
QueryRef
- valueChanges (Observable)
- subscribeToMore()
- refetch()
- fetchMore()
- start/stopPolling()
query
// app.component.ts
@Component()
export class App {
constructor(private apollo: Apollo){
let query = apollo.query({
query: gql`query todos {
allTodoes { id complete text } }`
});
let subscription = query.valueChanges.subscribe({
next: result => {
// result.loading
// result.data.allTodoes
},
error: error => {
console.log(`Error: ${error.message}`);
}
});
}
}
watchQuery
// todoList.component.ts
@Component({
template:
`<todo *ngFor="let todo of todos | async">{{todo.text}}</todo>`
})
class TodoList implements OnInit {
todos: Observable<any>;
constructor(private apollo: Apollo) { }
ngOnInit() {
const query = this.apollo.watchQuery({ query: todosQuery });
this.todos = query.valueChanges.map(({data}) => data.allTodoes);
}
// onRefresh = query.refetch;
}
mutate
// todo.component.ts
@Component()
export class Todo {
constructor(private apollo: Apollo) { }
private onTodoClick(){
let toggle$ = this.apollo.mutate({
mutation: gql`
mutation toggleTodo($id: ID!, $complete: Boolean!) {
updateTodo(id: $id, complete: $complete) { id text complete }
}`,
variables: { id: this.id, complete: !this.complete },
let subscription = toggle$.subscribe(
({ data }) => { // data.updateTodo.id }
);
}
}
demo
Adding Realtime
GraphQL Server
source: blog
Dependencies
- Apollo link for Websockets
- WebSocket subscriptions
Setup
export class GraphQLModule {
constructor(
private apollo: Apollo,
private httpLink: HttpLink
) {
const link = this.setupLink();
const cache = new InMemoryCache();
apollo.create({ link, cache });
}
}
Setup
private setupLink() {
const http = this.httpLink.create({
uri: GRAPHCOOL_URI
});
const websocket = new WebSocketLink(
new SubscriptionClient(GRAPHCOOL_URI_WS, {
reconnect: true
})
);
const link = ApolloLink.split(
(op) => {
const ast = getOperationAST(op.query, op.operationName);
return ast && ast.operation === 'subscription';
},
/* if true use */ websocket,
/* if false use */ http
);
return link;
}
Todos Schema
schema {
subscription: Subscription
}
type Mutation {
createTodo(text: String!, complete: Boolean!): Todo
deleteTodo(id: ID!): Todo
}
type Subscription {
Todo(filter: [CREATED, UPDATED, DELETED]): {
mutation: [CREATED, UPDATED, DELETED]
node: Todo
updatedFields: [String!]
previousValues: Todo
}
}
subscribeToMore
// app.component.ts
query.subscribeToMore({
document: gql`
subscription {
Todo(filter: { mutation_in: [CREATED] }) {
node {
id
complete
text
}
}
}
`,
updateQuery: (state: Todos, { subscriptionData }) => {
...
}
subscribeToMore
// app.component.ts
updateQuery: (state, { subscriptionData }) => {
const {node: node} = (subscriptionData as any).Todo;
if (!state.allTodoes.find(todo =>
todo.id === node.id)) {
state.allTodoes.push(node);
}
return {
allTodoes: todos
}
}
demo
Why use GraphQL?
Some reasons
- Declarative
- De-coupled from storage
- Validated and structured
- Facilitates Collaboration
- Super fast
More
gsans/todo-apollo-v2
Test driving Apollo Client 2.0
By Gerard Sans
Test driving Apollo Client 2.0
The Apollo team just released the new version of the Apollo Client. There's already a lot of hype around the new architecture using a new modular and flexible approach. In this talk we are going to put it to test together with Angular (v5+). Buckle up!
- 2,846