Tales of a React Native Beginner: Why GraphQLđ Is The Real MVP
In the previous episode
This article follows the previous one I wrote about my React Native and JavaScript learning experience. To fully understand all the references Iâll make in this article, I would recommend to have a look at it.
TL;DR: I started JavaScript and React Native about a year ago. It was hard, painful, I suffered, a lot⊠Just kidding: the experience was top notch (and still is!). So throughout this so called âYear of learningâ, I had the opportunity to work on 5~6 real-world apps. And today weâll focus on one of them, Wino đ·: the app Iâve spent the most time on to this day!
WinoâŠwait what?
I have to give them this one: my friends really nailed it with the naming đ ⊠2 years ago, 5 friends of mine started this adventure with a simple goal in mind:
Create the app that will tell you what wine you should buy, according to where and when youâll be drinking it.
This is how Wino was born. They launched the 1st beta of the app back in 2015, and everything was going well!
In July 2016, my brother in arms LĂ©o Le Bras came up to me and asked if Iâd like to work on the v1 with him. And gosh did he have great plans about what weâll be doing. As soon as I heard the worlds âReact Native ⊠Apollo Client ⊠Apollo Server ⊠GraphQL âŠâ coming out of his mouth I went full:
GraphQL: the real MVP
The main reason Wino distinguishes itself from any other apps Iâve worked on is fairly simple: itâs the 1st React Native project where I used GraphQL. For those who are not familiar with it:
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. -graphql.org
Iâll suggest you to read Give it a REST: use GraphQL for your APIs by David Celis to understand whatâs GraphQL, and why itâs more interesting than REST
People usually call every interaction with a GraphQL server a âqueryâ. But the language supports 3 kinds of ârequestsâ: mutations, subscriptions and ⊠queries! Thatâs why weâll talk about âGraphQL operationsâ. Sashko Stubailo from the Apollo core team broke down the anatomy of a GraphQL operation in this nice article.
As you can see, GraphQL lets you write some kind of JSON-ish operation and answers with the same format as your operation, plus your data. The possibilities given by this bad boy are pretty exciting, especially when youâre working on a mobile app! The app could fetch the exact amount of data it needs, when it needs it, and nothing else!
In order to use GraphQL in JavaScript, we choose Apollo Client for the app, and Apollo Server for ⊠well, the servers. As you may have guessed, Apollo is a JavaScript GraphQL client.
From there, something really impressed me with this React Native # GraphQL combo:
Fetching, storing and using data is incredibly easy, no matter the quantity!
There are several levels of âcomplexityâ with GraphQL operations, but if you keep things simple, hereâs what it looks like:
As you can see, we are just asking whoâs the hero of 2 different Star Wars movies, using arguments in our selection set (thatâs how we call the content of a GraphQL operation). And then, GraphQL returns a data object that follows the same pattern as the operation. Easy peasyđ
But wouldnât the best way to prove how cool and powerful this is to show it to you? Wino has a specific page where you can set up the city where you currently are. Here, we need citiesâ name and background pattern as props of our Carousel component. As you can see, itâs a very simple example, but we could start from there. Well: letâs build our GraphQL operation for this part of the app!
Letâs start by writing something similar to our Star Wars example. Our operation should look like:
{
city {
id
image
location
name
}
}
Sounds like a good way to start. But what if our operation grows with time? What if we decide to also fetch every wine cellar in each city at the same time, or something more complicated? In order to solve these kind of problems where your operation could be too complex, GraphQL allows us to use fragments.
Fragments let you construct sets of fields, and then include them in operations where you need to.
So fragments are like Lego you can use to build a complex operation. For now, we seem to have a simple one, so we wonât need it. But at least you know they are there if you do! So as with any kind of request, weâll break it down into 3 simple steps:
A. Write the operation
B. Fetch the data
C. Use the data
A. The Operation
For this part, weâll need a new package named graphql-tag. Itâs a JavaScript template literal tag that parses GraphQL operation strings into the standard GraphQL AST. Our file will look like this:
import gql from âgraphql-tagâexport const getAllCities = gql`
query getCities {
cities {
id
image
location
name
radius
}
}
`
Thatâs what a GraphQL operation looks like, a simple object without its value. We just exported getAllCities() that contains the real query getCities inside of it. As you can see, itâs pretty straightforward!
By the way, if you wonder what kind of sorcery this gql`âŠ` syntax is, itâs an ES2015 feature called tagged template literals. Max Stoiber uses it in styled-components đ and wrote a great article about it!
B. The Data
Fragment â , operation â : we just need to fetch our data then! For this specific part, why not use a HOC (Higher-Order Component)? Not that we absolutely need it here, just to show that you can use Apollo with it.
A higher-order component is a function that takes a component and returns a new component. -Reactâs doc
By the way, Sashko Stubailo also wrote a great article about using Apollo & recompose to get nice and beautiful HOCs in your React components: you should definitely check it out!
import { compose } from 'recompose'
import { graphql } from 'react-apollo'
import { withRouter } from 'react-router-navigation'import { getAllCities } from './queries'export default (component) => compose(
withRouter,
graphql(getAllCities),
)(component)
So our HOC -letâs call it connect- will take a React component as a parameter, add another HOC (withRouter) and the result of our query to its props, and then output a new component. So long story short:
Our GraphQL result is waiting for us in this.props.data.cities!
C. The Result
We just have to get cities out of this.props.data and weâre good to go:
import connect from './connect'const CitiesContainer = ({ children, ...props }) => children(props)
export default connect(CitiesContainer)
If you have noticed in our connect file, graphql() is also a HOC. It means that if we donât want to use another HOC, we could write this component like this:
import { graphql } from 'react-apollo'
import { getAllCities } from './queries'const CitiesContainer = ({ children, ...props }) => children(props)export default graphql(getAllCities)(CitiesContainer)
But Tte good news is that now we can freely access each cityâs name and background patterns we needed for the Carousel component:
class App extends Component {
// ... render() {
<CitiesContainer>
{({ data: { cities } }) => (
<Carousel cities={cities} />
)}
</CitiesContainer>
}
}
And surprise: thatâs it, weâre done!
And the best thing about this is that you donât even need to bother with Redux: Apollo is shipped with it right out the box! By default, Apollo Client creates its own internal Redux store to manage operations and their results. But obviously, you can also use your own Redux reducers, and itâs super simple. The only thing you need is to set up your ApolloProvider:
import React from 'react'
import { AppRegistry } from 'react-native'
import ApolloClient from 'apollo-client'
import { ApolloProvider } from 'react-apollo'
import { createStore, combineReducers } from 'redux'import { userReducer } from '@store/reducers'const client = new ApolloClient()const store = createStore(
combineReducers({
users: userReducer,
apollo: client.reducer(),
}),
{}, // initial state
)const App = () => (
<ApolloProvider store{store} client={client}>
<MyRootComponent />
</ApolloProvider>
)AppRegistry.registerComponent('MyApp', () => App)
So thatâs how we use GraphQL in a nutshell and I find it pretty straightforward! GraphQL is a very powerful query language and we had the opportunity to admire its full potential in a mobile app.
Basically when you build a mobile app, the Holy Grail is to reduce network requests as much as you can, without impacting the user experience. No relentless loading screens.
With GraphQL (and Apollo by extend), whenever you make an operation, the request and its result are stored. Whenever youâll make the same operation, Apollo will apply it on its store, instead of your GraphQL server. Nevertheless, you can specifically ask it to fetch fresh data from the server.
// We use the built-in method to refetch data when needed
const onRefreshClicked = () => {
this.props.data.refetch()
}// Here Apollo automatically refetches data every 10s
const feedWithData = graphql(feedEntries, {
options: { pollInterval: 10000 },
})(Feed)
Hence, youâll have less loading screens than before and moreover, you can persist Apolloâs store into local storage (AsyncStorage in React Native) for offline purposes.
Well, things become even more interesting with graphql-anywhere. The description speaks for itself:
Run a GraphQL query anywhere, without a GraphQL server or a schema. Just pass in one resolver. Use it together with graphql-tag.
Yes, youâve read it right. This package let you write GraphQL anywhere ⊠even for your own ânormalâ Redux store! You can even filter/normalize the result of an operation, even if itâs not from a GraphQL server!
Once again, I hand it over to Sashko Stubailo with his great article about graphql-anywhere.
If you want to give it a closer look, Wino đ· is already available on iOS through the link down there (the Android version will be rolling out in a couple of weeks). Kudos to all the guys at Facebook and Meteor for giving us GraphQL and Apollo!
Thanks for taking the time to read my article, I hope you found it interesting. Very special thanks to ThĂ©o Kunetz & LĂ©o Le Bras for reviewing it, and hello JS & React Amsterdam teams for this opportunity! If you have any question or feedback: Iâm 24/7 on Twitter đ»!