Lit  Apollo

Data-Driven Components that Use the Platform

(Space, HJKL or Keys to Navigate)

(F for Fullscreen)

GraphQL

Query Language for APIs

Published by Facebook in 2015

Tradeoffs ⚖️

✅ Pros

  • Strongly Typed at Runtime
  • Highly Abstract and Expressive
  • Deeply Nested Data is Easy to Manage
  • Fields Defined by Functions Called Resolvers

😕 Cons

  • Implementation Overhead
  • Unfamiliar Debugging Story
  • Performance Pitfalls
  • Rife with Gotcha's

Queries and Mutations

Unlike REST APIs, GraphQL exposes a single endpoint.

Consumers read data from the graph with Queries, and change data on the graph with Mutations

By analogy to REST:

GET
query
POST,
PUT,
DELETE
mutation

Queries

Even if the database schema doesn't match your query...

...you can still respond sensibly.

The GraphQL Schema tells your server the type of each object.

The resolvers define the contents of each field.

Apollo

An opinionated framework for implementing GraphQL Apps

Apollo Client

  • Front-end data store
  • "Redux", but GraphQL
  • Network & Client State

Apollo Server

  • NodeJS Framework
  • Quickly Bootstrap your Server
  • Deploy to Cloud Providers

Apollo Client

Stores its data in a normalized cache

Manage local state with GraphQL queries and mutations

Apollo Elements

Base-classes and Mixins for many Web Component Libraries

Apollo Client, but web components
Super Nintendo Chalmers

lit-apollo

👩‍🚀 Apollo for Data Layer

🚒 LitElement for View Layer & Reactivity

✅ TypeScript typings

🤝 Pairs well with...

  • rollup-plugin-graphql
  • rollup-plugin-lit-css
  • graphql-codegen

ApolloQuery base class

file tree with three files: person-element.ts, person-element.css, and Person.query.graphql

Mutations

Write UI components that update your graph

Mutations

Say we had a list component that should toggle an `isBestFriend` flag on any member of the list.

Let's define <toggle-friend> as a mutation component!

Mutations - DOM

Mutations - Optimistic UI

Mutations - Updating the Cache

Cool Tricks

Lazy-Loaded Resolvers

Only load resolvers when components are needed.

Leverage the PRPL-pattern to keep the UX snappy.

GraphQL Error Handling

Since they are nested and resolved by field, GraphQL queries can come back with…

  • partial data, and/or
  • partial errors

You have a lot of flexibility in how to handle them.

Handle GraphQL Errors

Bear in mind that errors in your client-side resolvers will be reported as networkErrors.‍️

The classic Picard face-palm

Let's write a quick mixin to help us when debugging!

Gotchas

The Limitations of GraphQL

Some things just can't be expressed in GraphQL's Schema Definition Language, e.g. unions of scalar types.

For example, we couldn't represent a Highchart chart in GraphQL, because this PointOptionsType union contains both scalar and object types.

The best we could hope for would be to wrap scalars in object types:

And we'd still have to parse the response on the client.

Null-Checks Everywhere

GraphQL type properties are optional by default. Non-nullability is opt-in.

This general programming gripe takes on acute significance when your app deals in deeply nested optionals.

Null-Checks Everywhere

Different languages have different constructs to help with this, e.g Maybe.

In JavaScript, get comfy with the Optional Chaining (?.) and Nullish Coalescing (??) operators.

Forgetting __typename

GraphQL objects must come with a __typename property. Omitting it when writing data to the Apollo cache throws an error.

It may not be obvious from the stack trace where the error originates, so make sure to always include the __typename.

writeToStore.js:106 Missing field __typename in {
  "amount": 100,
  "currency": "gbp"
},

(anonymous) @ writeToStore.js:106
writeSelectionSetToStore @ writeToStore.js:89
writeFieldToStore @ writeToStore.js:200
(anonymous) @ writeToStore.js:96
writeSelectionSetToStore @ writeToStore.js:89
writeResultToStore @ writeToStore.js:69
replaceQueryResults @ replaceQueryResults.js:13
data @ store.js:133
apolloReducer @ store.js:46
combination @ combineReducers.js:120
dispatch @ createStore.js:165
(anonymous) @ ApolloClient.js:174
(anonymous) @ store.js:18
ObservableQuery.updateQuery @ ObservableQuery.js:223
(anonymous) @ ObservableQuery.js:142
            

Forgetting to query for ID

The Apollo cache is normalized by object ID and__typename. So if you already have an object in your cache, apollo won't fetch it twice unless you tell it to. This means that if you query for an object and its ID once, you have to always query for it by ID.

Network error: Error writing result to store for query

query PokemonQuery($id: ID) {
  Pokemon(id: $id) {
    name
    url
    createdAt
  }
}

Store error: the application attempted to write an object with no provided
id but the store already contains an id of Pokemon:ciwnmyvxn94uo0161477dicbm
for this object.
          

Deeply Updating After Mutations

If your graph has calculated fields that depend on deep properties as their inputs, they will need to update whenever the input changes.

Apollo Client 3 will introduce "reactive variables" to help with this, but for now, you'll have to manually recalculate the dependent fields when you mutate ther inputs.

Thank You!