Mocking schema capabilities
Start on client-side features before your server supports them
If your GraphQL server doesn't yet support a field that your client will use, you can still start building against that field by mocking its behavior within the client.
For example, let's say we want to add a feature to the Space Explorer app from the full-stack quickstart course. Specifically, we want to display a description of the rocket used for each launch. To support this functionality on the server side, we'll add a description
field to our schema's Rocket
type:
1type Rocket {
2 id: ID!
3 name: String
4 type: String
5 description: String # field not yet supported
6}
But what if our back-end team isn't finished adding support for the description
field? By mocking the field's behavior, we can still start developing the feature in our client. To do so, we'll follow the steps below.
1. Define a client-side schema (recommended)
Our client application can define a client-side schema that extends types from our server, or even defines entirely new types. The syntax is identical to server-side schema definitions.
Although a client-side schema isn't required for mocking, it helps team members understand your app's local capabilities. It also unlocks powerful local state support in tools like the Apollo Client Devtools and the Apollo extension for VS Code.
This client-side schema extends the Rocket
type to add a description
field (make sure to name the variable typeDefs
as shown):
1const typeDefs = gql`
2 extend type Rocket {
3 description: String
4 }
5`;
We can then provide this schema to the ApolloClient
constructor, like so:
1const client = new ApolloClient({
2 uri: 'http://localhost:4000/graphql',
3 cache: new InMemoryCache(),
4 typeDefs
5});
2. Define a read
function
Our client app doesn't yet know how to populate the Rocket.description
field. To fix this, we can define a read
function for the field. The Apollo Client cache calls this function whenever the field is queried, and the function's return value is used as the field's value.
Let's define our read
function in the configuration object we provide to the InMemoryCache
constructor:
1const cache = new InMemoryCache({
2 typePolicies: {
3 Rocket: {
4 fields: {
5 description: {
6 read() { // Read function for Rocket.description
7 return 'Placeholder rocket description';
8 }
9 },
10 },
11 },
12 },
13});
This enables us to query the field, but we might not want to show the same boilerplate description for every rocket. To add variety to our mocked output, we can use a library like faker.js:
1import { faker } from "@faker-js/faker";
2
3// Returns 1 or 2 sentences of Lorem Ipsum
4const oneOrTwoSentences = () =>
5 faker.lorem.sentences(Math.random() < 0.5 ? 1 : 2);
We can then update our read
function like so:
1// (within InMemoryCache constructor)
2read() {
3 return oneOrTwoSentences();
4}
Make sure to include libraries like faker.js only in your development build, because they can needlessly increase your production bundle size.
3. Query with the @client
directive
We're ready to execute a query that includes our new field. Here's an abridged GET_LAUNCH_DETAILS
query from the full-stack quickstart with our description
field added:
1export const GET_LAUNCH_DETAILS = gql`
2 query LaunchDetails($launchId: ID!) {
3 launch(id: $launchId) {
4 site
5 rocket {
6 type
7 description @client
8 }
9 }
10 }
11`;
Notice that this field includes the @client
directive. This directive tells Apollo Client not to include description
in the query it sends to our server. This is important for two related reasons:
The
description
field is populated entirely locally, so including it in network requests isn't helpful.The
description
field isn't in our server-side schema yet, so including it will produce a GraphQL error.
We can now execute this query in a component with the useQuery
hook as usual:
1export default function LaunchDetails({ launchId }) {
2 const { data } = useQuery(GET_LAUNCH_DETAILS, { variables: { rocketId } });
3 return (
4 <div>
5 <p>Rocket Type: {data.launch.rocket.type}</p>
6 <p>Description: {data.launch.rocket.description}</p>
7 </div>
8 );
9}
4. Use live data when ready
When your server's support for the Rocket.description
field is ready, you can begin using it by doing the following:
Remove the
@client
directive fromdescription
in every query that includes it.Remove the field's
read
function (or modify the function so that it uses the current cached value instead of a random string).
For more information on the Apollo Client features used here, see the following: