React: Prototyping with Apollo Client Schemas
Tags: graphql, javascript, react
Published: 2020-08-02 19:04:41

---
title: React: Prototyping with Apollo Client Schemas
published: true
description: Learn how to include Apollo into your prototypes before setting up a server
tags: graphql, javascript, react
//cover_image: https://direct_url_to_image.jpg
---

Building a prototype is a great way to validate an idea, or to gather feedback from users without taking on the risk of having to build out an entire application. In this tutorial we'll take a look at Apollo Client and how we can leverage a client side schema to set us up for success when we're ready to build out an API to talk to our front end.

Apollo Client is a tool used to manage your client side data. It's typically paired with Apollo Server but it will work with any GraphQL server implementation, which makes it great for prototyping. Even if we choose a different GraphQL implementation for our server like Absinthe later, we can still keep our front end queries as long as the schema is defined the way we're expecting.

For our demo we're going to create an app that will return some information about our user's location based on their IP Address. Let's get started!

First we'll spin up a react app and install apollo:

```bash
npx create-react-app apollo-client-schema-demo
cd apollo-client-schema-demo
npm i
npm install @apollo/client graphql
```
First let's create a component to display our user's information. We don't really need to worry about where the data is coming from right now so we'll use static data. Create an `IPInfo.js` file that looks like this:

```jsx
import React from "react";

const IPInfo = () => {
  const data = {
    ipAddress: "1.1.1.1",
    city: {
      name: "Sheboygan",
      population: 123456,
    },
    country: {
      name: "USA",
      population: 123456,
    },
  };

  return (
    <main className="App">
      <h1>Howdy!</h1>
      <p>Your IP Address is {data.ipAddress}</p>
      <p>
        {`Your city, ${data.city.name}, has a current population of
         ${data.city.population}`}
      </p>
      <p>
        {`Your Country, ${data.country.name}, has a current population of
         ${data.country.population}`}
      </p>
      <p>Cool, huh?</p>
    </main>
  );
};

export default IPInfo;

```

Let's also edit our `App.js` file to show this component:

```jsx
[...]
function App() {
  return (
      <div className="container">
        <IPInfo />
      </div>
  );
}
[...]
```
...and edit our `App.css` file slightly to clean it up:

```css
body {
  margin: 2rem;
}

.container {
  max-width: 800px;
  margin: auto;
}
```
If we run `npm start`, we should be greeted with something like this:

![IP Info Page](https://dev-to-uploads.s3.amazonaws.com/i/56v5uq6cqh6qibshvzuk.png)

Now we need to set up an apollo client. Add the following to `App.js`:

```jsx
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache(),
});

```

This sets up an instance of ApolloClient. The uri we chose is from the Apollo Documentation and can be used as a placeholder until we have a real server to point to. The contents of the server don't really matter since we'll only be pointing at our client schema, but it is a required field when instantiating a client.

In order to tie our app to apollo, we need to wrap it in an instance of `ApolloProvider`. To do that we'll need to edit our App component:

```jsx
function App() {
  return (
    <ApolloProvider client={client}>
      <div className="container">
        <IPInfo />
      </div>
    </ApolloProvider>
  );
}
```

If we refresh we shouldn't see any difference since we aren't actually querying for anything. To do that without having an actual server to call, we can define `typeDefs` in our app and pass them in to our client instantiation. Let's make some modifications to `App.js`: 

```jsx
import React from "react";
import "./App.css";

import {
  ApolloClient,
  ApolloProvider,
  InMemoryCache,
  gql,
} from "@apollo/client";
import IPInfo from "./IPInfo";

const typeDefs = gql`
  extend type Query {
    client: Client!
  }

  extend type Client {
    ipAddress: IPAddress!
  }

  extend type IPAddress {
    address: String!
    city: City
    country: Country
  }

  extend type City {
    name: String!
    population: Int
  }

  extend type Country {
    name: String!
    population: Int!
  }
`;

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache(),
  typeDefs,
});

function App() {
  return (
    <ApolloProvider client={client}>
      <div className="container">
        <IPInfo />
      </div>
    </ApolloProvider>
  );
}

export default App;

``` 

Here, we're defining `typeDefs` and creating a `client` query and some types to support it, then passing that in to our `client` constructor. Now we can use apollo's `useQuery` hook to fetch the results of that query, even though we still haven't written anything to resolve it and we haven't build out a server. Let's do that in `IPInfo.js`:

```jsx
import React from "react";
import { useQuery, gql } from "@apollo/client";

const CLIENT_QUERY = gql`
  {
    client @client {
      ipAddress {
        address
        city {
          name
          population
        }
        country {
          name
          population
        }
      }
    }
  }
`;

const IPInfo = () => {
  const {
    data: {
      client: { ipAddress: { address, city = {}, country = {} } = {} } = {},
    } = {},
    loading,
    error,
  } = useQuery(CLIENT_QUERY);

  if (loading) {
    return (
      <p>
        Hmm...{" "}
        <span role="img" aria-label="thinking emoji">
          🤔
        </span>
      </p>
    );
  }

  if (error) {
    return (
      <p>
        Ruh Roh{" "}
        <span role="img" aria-label="sad emoji">
          😫
        </span>
      </p>
    );
  }

  return (
    <main className="App">
      <h1>Howdy!</h1>
      <p>Your IP Address is {address}</p>
      <p>
        {`Your city, ${city.name}, has a current population of
         ${city.population}`}
      </p>
      <p>
        {`Your Country, ${country.name}, has a current population of
         ${country.population}`}
      </p>
      <p>Cool, huh?</p>
    </main>
  );
};

export default IPInfo;
```

We've changed a lot here, so let's step through.

First we define our graphql query. Nothing very special there if you're familiar with graphql, but note the `@client` directive. That tells apollo that this doesn't exist on the server, so there's no need to ask the server for this. 

In the actual component code we take advantage of apollo's `useQuery` hook to make our query:

```jsx
  const {
    data: {
      client: { ipAddress: { address, city = {}, country = {} } = {} } = {},
    } = {},
    loading,
    error,
  } = useQuery(CLIENT_QUERY);
```

This gives us all the data we need to power our form, plus a few variables to manage different query states. Our markup has remained largely the same, though we did add a bit to handle loading and error states.

If we refresh our page, we'll see a whole lot of nothing:

![IP Info Screen with no data](https://dev-to-uploads.s3.amazonaws.com/i/b8thuhhj94ignucqrure.png)

Why is that? Well, in our client schema we only defined the shape of our data, but not it's contents. To do that we need to create a resolver. Let's add one right underneath our schema in App.js:

```jsx
const resolvers = {
  Query: {
    client: () => ({
      ipAddress: {
        address: "172.220.20.36",
        city: {
          name: "Sheboygan",
          population: 48895,
        },
        country: {
          name: "United States of America",
          population: 325145963,
        },
      },
    }),
  },
};

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache(),
  typeDefs,
  resolvers,
});

```

Don't forget to add your `resolvers` object to your client.

In our resolver, we defined what should be returned when something calls the `client` query. We could make this more random if we wanted, but this will suit our prototype just fine. Now if we refresh we see the data from our resolver:

![IP Info screen with mocked data](https://dev-to-uploads.s3.amazonaws.com/i/7x9oy5p837al9i9bqpby.png)

Let's say in parallel we did some research and found out there was a site, [everbase.co](https://www.everbase.co/), that had a schema that perfectly matched our client query. What a coincidence! All we have to do now is update our client url and remove the `@client` directive from our query and voila, we have an app connected to real data.

By doing the work of setting up our client and mocking our queries out up front, we wind up laying a lot of infrastructure necessary to complete our application when the time comes. If you'd like to see our demo in action it can be found [here](https://y-hello-thar.netlify.app/), or you can check out the source [here](https://github.com/chris-held/apollo-client-schema-demo). If you'd like to do some further research [the Apollo docs](https://www.apollographql.com/docs/react/local-state/client-side-schema/) are a great resource. Thanks for reading!