GraphQL and React (Native)
Kadi Kraman
@kadikraman
The urql edition
Kadi Kraman
Engineering Manager
Formidable
Topics
Exchanges
Caching
Authentication
What is urql all about?
Dev Tools
Offline Support
Community
Apollo Client
GraphQL Clients for React (Native)
urql
Relay
What is urql all about?
our place in the ecosystem was to challenge existing solutions and to be flexible enough to adopt new ideas
- Phil Plückthun, urql core maintainer
- alternative to Apollo Client
- first launched ~2 years ago
- fast, reliable, extensible, comprehensive
My experience with GraphQL + RN
First RN + gql project
August 2020
(using Apollo Client)
(using urql)
October 2017
Latest RN + gql project
.
.
.
.
various RN projects with and without GraphQL
. . . . . . .
. . . . . . .
. . . . . . . . . . .
Getting started
# npm
npm i --save urql graphql
# or yarn
yarn add urql graphql
import { createClient, Provider } from 'urql';
const client = createClient({
url: 'https://0ufyz.sse.codesandbox.io'
});
const App = () => (
<Provider value={client}>
<Todos />
</Provider>
);
Install
Create the client
npm install @apollo/client graphql
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
} from '@apollo/client';
const client = new ApolloClient({
uri: 'https://48p1r2roz4.sse.codesandbox.io',
cache: new InMemoryCache()
});
function App() {
return (
<ApolloProvider client={client}>
<div>
<h2>My first Apollo app 🚀</h2>
</div>
</ApolloProvider>
);
}
Install
Create the client
urql
Apollo
import { useQuery } from 'urql';
const Todos = () => {
const [res] = useQuery({
query: `
query { todos { id text } }
`,
});
if (res.fetching) return <p>Loading...</p>;
if (res.error) return <p>Errored!</p>;
return (
<ul>
{res.data.todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
};
Query Data
import { useQuery, gql } from '@apollo/client';
const EXCHANGE_RATES = gql`
query GetExchangeRates {
rates(currency: "USD") {
currency
rate
}
}
`;
function ExchangeRates() {
const { loading, error, data } = useQuery(EXCHANGE_RATES);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return data.rates.map(({ currency, rate }) => (
<div key={currency}>
<p>
{currency}: {rate}
</p>
</div>
));
}
urql
Apollo
Exchanges
Exchanges are a series of operations that transform the input (operations) stream to the output stream
Similar to Apollo link
it [urql] is so extensible that even the cache is an exchange
- Jovi De Croock, urql core maintainer
icons from flaticon.com
useQuery
data
How do Exchanges work?
fetchExchange - sends an operation to the API using fetch and adds results to the output stream
fetchExchange
useQuery
data
dedupExchange
dedupExchange - remove duplicate from pending operations
fetchExchange
useQuery
data
cacheExchange - the default caching logic with "Document Caching"
dedupExchange
fetchExchange
useQuery
data
cacheExchange
cacheExchange
dedupExchange
useQuery
data
fetchExchange
your own exchange with custom enhancements
cacheExchange
dedupExchange
useQuery
data
fetchExchange
Creating custom exchanges
import { createClient, dedupExchange, fetchExchange } from 'urql';
const noopExchange = ({ client, forward }) => {
return operations$ => {
// <-- The ExchangeIO function
// We receive a stream of Operations from `dedupExchange` which
// we can modify before...
const forwardOperations$ = operations$;
// ...calling `forward` with the modified stream. The `forward`
// function is the next exchange's `ExchangeIO` function, in this
// case `fetchExchange`.
const operationResult$ = forward(operations$);
// We get back `fetchExchange`'s stream of results, which we can
// also change before returning, which is what `dedupExchange`
// will receive when calling `forward`.
return operationResult$;
};
};
const client = createClient({
exchanges: [dedupExchange, noopExchange, fetchExchange],
});
https://formidable.com/open-source/urql/docs/concepts/exchanges/
Implementing Authentication
urql has built in support for auth!
https://formidable.com/open-source/urql/docs/api/auth-exchange/
... and an Authentication guide
Caching
Document Cache
Default cache, good for simple applications
Enabling fetching the data from cache instead of querying anew each time
Request policy
import { useQuery } from 'urql';
const [res] = useQuery({
query: `
query { todos { id text } }
`,
requestPolicy: "cache-and-network",
});
cache-first (default)
cache-and-network, cache-only, and network-only
Cache invalidation
https://www.npmjs.com/package/@urql/exchange-request-policy
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
import { requestPolicyExchange } from '@urql/exchange-request-policy';
const client = createClient({
url: 'http://localhost:1234/graphql',
exchanges: [
dedupExchange,
requestPolicyExchange({
// The amount of time in ms that has to go by before upgrading, default is 5 minutes.
ttl: 60 * 1000, // 1 minute.
// An optional function that allows you to specify whether an operation should be upgraded.
shouldUpgrade: operation => operation.context.requestPolicy !== 'cache-only',
}),
cacheExchange,
fetchExchange,
],
});
Graphcache
More sophisticated cache, handles interdependencies!
import { cacheExchange } from "@urql/exchange-graphcache";
const client = new Client({
exchanges: [dedupExchange, cacheExchange({}), fetchExchange],
});
Handling cache updates after a mutation
const [res] = useQuery({
query: `
query { todos { id text } }
`,
});
const UpdateTodo = `
mutation ($id: ID!, $title: String!) {
updateTodo (id: $id, title: $title) {
id
title
}
}
`;
const Todo = ({ id, title }) => {
const [updateTodoResult, updateTodo] = useMutation(UpdateTodo);
};
refetchQueries?
in React Apollo
refetchQueries?
manually update cache with mutation result?
export const createPostUpdate = (cache, { data }) => {
if (!data || !data.createPost) {
return;
}
const createPost = data.createPost;
const variables = { groupId: createPost.feedItem.data.group_id || undefined };
const cachedFeed = cache.readQuery({
query: GET_FEED,
variables
});
const existing = cachedFeed.feed.items.path === createPost.feedItem.path;
if (!existing) {
cache.writeQuery({
query: GET_FEED,
variables,
data: {
...cachedFeed,
feed: {
...cachedFeed.feed,
items: [createPost.feedItem, ...cachedFeed.feed.items]
}
}
});
}
};
refetchQueries?
manually update cache with mutation result?
let the cache handle it?
With Graphcache, the cache is automatically updated with the mutation result
Every piece of data should have a __typename and id
const UpdateTodo = `
mutation ($id: ID!, $title: String!) {
updateTodo (id: $id, title: $title) {
id
title
}
}
`;
If the data doesn't have a reliable id
You can add it manually in the cache config
Or opt out of the cache for these values
const cacheExchangeConfig = {
keys: {
SomeFieldWithoutId: data => /* calculate id here based on parent */,
},
}
const cacheExchangeConfig = {
keys: {
SomeFieldWithoutId: () => null,
},
}
What if the mutation result doesn't return everything that gets updated?
You can also define manual update queries if you need to
import { cacheExchange } from "@urql/exchange-graphcache";
const cacheExchangeConfig = {
updates: {
updateToDoListComplete: (
mutationResult: CompleteToDoType,
_: any,
cache: any,
) => {
// if the user completed the last item in their ToDo list, remove it from their profile
if (mutationResult.updateTodoListComplete.completedPercentage === 1) {
cache.updateQuery({ query: currentUserQuery }, (data: any) => {
data.currentUser.currentToDoList = null;
return data;
});
}
},
}
}
const client = new Client({
exchanges: [dedupExchange, cacheExchange(cacheExchangeConfig), fetchExchange],
});
Offline Support with Graphcache
urql devtools
https://github.com/FormidableLabs/urql-devtools
Setup
# npm
npm i @urql/devtools
# yarn
yarn add @urql/devtools
Install the exchange:
Add the exchange to your urql client:
import { createClient, defaultExchanges } from 'urql';
import { devtoolsExchange } from '@urql/devtools';
const client = createClient({
url: 'http://localhost:3001/graphql',
exchanges: [devtoolsExchange, ...defaultExchanges],
});
npx urql-devtools
Then to launch the dev tools:
or install the extension on Chrome or Firefox if on the web
Schema explorer
Query / Mutation timeline
Playground
Community
GitHub Discussions
Thank you!
https://formidable.com/open-source/urql
https://github.com/FormidableLabs/urql/discussions
urql community
urql docs
https://github.com/FormidableLabs/urql-devtools
urql devtools
https://slides.com/kadikraman/urql-graphql-madrid/fullscreen
Slides
@kadikraman
GraphQL with React (Native) - The urql edition
By Kadi Kraman
GraphQL with React (Native) - The urql edition
Talk for GraphQL Madrid https://www.meetup.com/GraphQLMadrid/events/273165807/
- 912