Author: Richard Haines

Posted: 11 May 2020

take me there

  • Setup
  • Adding Apollo
  • Our schema
  • Graphql

Setup

Im currently working on a project which will be a mobile app for parents to swap unused items. To do this im using Expo with a FaunaDB database. I had already setup a project with Fauna and Gatsby so thought the knowledge transfer would be quite simple. And it was, until it wasn't.

The problem with trying to access the Fauna database from a React Native (RN) app is that RN doesn't come with or support the Node Standard Library, which Fauna uses. The solution, or the one I found, is to create a separate API to handle the Fauna side of things and access that API form the app. Let's go through a simple setup to see how this is done.

Make sure you have installed the expo-cli and now-cli.

1yarn global add expo-cli
2yarn global add vercel

Create a new expo project, follow the instructions (you can choose a blank or tabs template) and open your code editor

1expo init myProject
2cd myProject
3code .

Ok, leave the app for a minute and login to your Fauna account, create a new database and name it whatever you like. Perhaps MySuperDB? Create a new key with a server role. Name it FAUNA_SERVER. Make a note of it as it wont be shown again.

Back to our app we now need to add some packages. Its a long list so just do it all in one.

1yarn add @apollo/client apollo-link-context apollo-link-http graphql graphql-tag isomorphic-fetch
2
3yarn add react-native-dotenv -D

Adding Apollo

First we want to setup our Apollo Provider which will wrap our root component, App.js. Create a new file in the constants folder and name it apollo.js

1const React = require("react");
2import { FAUNA_SERVER } from "react-native-dotenv";
3const {
4 ApolloProvider,
5 ApolloClient,
6 InMemoryCache
7} = require("@apollo/client");
8const { setContext } = require("apollo-link-context");
9const { createHttpLink } = require("apollo-link-http");
10const fetch = require("isomorphic-fetch");
11
12const httpLink = createHttpLink({
13 uri: "https://graphql.fauna.com/graphql",
14 fetch
15});
16
17const authLink = setContext((_, { headers }) => {
18 return {
19 headers: {
20 ...headers,
21 authorization: `Bearer ${FAUNA_SERVER}`
22 }
23 };
24});
25
26const client = new ApolloClient({
27 link: authLink.concat(httpLink),
28 cache: new InMemoryCache()
29});
30
31const Apollo = ({ children }) => (
32 <ApolloProvider client={client}>{children}</ApolloProvider>
33);
34
35export default Apollo;

The above code can be boiled down to:

  • Created a new http link
  • Create an auth link which returns the headers to the context so the http link can read them. Here we pass in our Fauna key with server rights.
  • Then we create the client to be passed to the provider with the link now set as the auth link.

Create a new .env file at the projects root and add your fauna server key, using the name we gave it in our fauna dashboard.

1FAUNA_SERVER=xxxxxxxxxxxxxxxxxxxxxxxxxxx

Next head over to App.js and import and wrap the App with our new Apollo component. (I had used the tab template so yours will look a bit different if you chose the blank templete)

1import Apollo from "./constants/apollo";
2//.....lots of code
3
4return (
5 <Apollo>
6 <View style={styles.container}>
7 {Platform.OS === "ios" && <StatusBar barStyle="dark-content" />}
8 <NavigationContainer linking={LinkingConfiguration}>
9 <Stack.Navigator>
10 <Stack.Screen name="Root" component={BottomTabNavigator} />
11 </Stack.Navigator>
12 </NavigationContainer>
13 </View>
14 </Apollo>
15);

Our schema

Now we can add a schema, which we will import into our Fauna database via their dashboard. Inside the constants folder create a new file named schema.gql.

1type Query {
2 allUsers: [User]
3}
4
5type User {
6 name: String!
7 email: String!
8}

Back on the Fauna dashboard click the graphql tab on the left and import the schema. Once done you will be presented with a graphql playground. Add some users to our database.

1mutation {
2 createUser(
3 data: {
4 name: "Richard Haines"
5 email: "hello@richardhaines.dev"
6 }
7 ) {
8 _id
9 name
10 email
11 }
12 }

Ok cool, now we can setup our serverless function. Create a new project and initialize it.

1mkdir mySuperAPI
2cd mySuperAPI
3yarn init -y

Vercel expects us to have an api folder so create that. We will also want to install some packages here.

1yarn add @now/node apollo-link-http apollo-server-micro dotenv graphql graphql-tag isomorphic-fetch

Inside the api folder create a new folder that begins with an underscore, this is so that vercel knows not to build it's content as a functions. Inside that folder create a new file named config.js.

1cd api
2mkdir _utils
3cd _utils && touch config.js

Create a .env file at the root and add the Fauna server key. Then in the new config.js file add the following. This is basically a short cut for us having to type out process.env.MY_KEY. Right now we only have one key but we might want to add more later. I find this a handy little file to use.

1const dotenv = require("dotenv");
2dotenv.config();
3module.exports = {
4 faunaServerKey: process.env.FAUNA_SERVER
5};

Graphql

Back to the api folder create a new file called graphql.js and add the following.

1const { faunaServer } = require("./config");
2const { createHttpLink } = require("apollo-link-http");
3const {
4 ApolloServer,
5 makeRemoteExecutableSchema,
6 introspectSchema
7} = require("apollo-server-micro");
8const fetch = require("isomorphic-fetch");
9
10const link = createHttpLink({
11 uri: "https://graphql.fauna.com/graphql",
12 fetch,
13 headers: {
14 Authorization: `Bearer ${faunaServer}`
15 }
16});
17
18const schema = makeRemoteExecutableSchema({
19 schema: introspectSchema(link),
20 link
21});
22
23const server = new ApolloServer({
24 schema,
25 playground: true,
26 introspection: true
27});
28
29module.exports = (req, res, ...args) => {
30 if (req.method === "OPTIONS") return res.status(200).send();
31
32 const handler = server.createHandler();
33
34 return handler(req, res, ...args);
35};

Run now from the mySuperAPI root and deploy the API. Head back over to the app and open the HomeScreen.js file located in the screens folder. Import gql and the useQuery hook from Apollo. Add a query to get all the users you just created and use the useQuery hook to display the returned data.

1import { gql, useQuery } from "@apollo/client";
2
3const GET_USERS = gql`
4 query GetUsers {
5 allUsers {
6 data {
7 name
8 location
9 _id
10 }
11 }
12 }
13`;
14
15export default function HomeScreen() {
16
17 const {
18 loading,
19 error,
20 data
21 } = useQuery(GET_USERS);
22
23 return (
24 <View style={styles.container}>
25 <ScrollView
26 style={styles.container}
27 contentContainerStyle={styles.contentContainer}
28 >
29 <View style={styles.getStartedContainer}>
30
31 {!loading && !error && data.allUsers.data.map((user) => (
32 <Text key={user._id}>{user.name}</Text>
33 ))}
34 </ScrollView>
35 </View>
36 );
37}

Now its time to run the app and bask in your glory!

1yarn start

You have multiple options to view your app, the easiest is to just scan the QR code on the metro bundler browser screen.

You now have a working mobile app which is pulling data with Apollo from FaunaDB via a serverless API hosted on Vercel. Yay for technology! 🤩

Edit on GitHub.Previous: Custom FaunaDB hooks for new projectNext: Notes on GSAP