# URQL GraphQL Client Documentation
# Source: https://raw.githubusercontent.com/urql-graphql/urql/main/docs/graphcache/schema-awareness.md
# Path: docs/graphcache/schema-awareness.md

---
title: Schema Awareness
order: 5
---

# Schema Awareness

Previously, [on the "Normalized Caching" page](./normalized-caching.md) we've seen how Graphcache
stores normalized data in its store and how it traverses GraphQL documents to do so. What we've seen
is that just using the GraphQL document for traversal, and the `__typename` introspection field
Graphcache is able to build a normalized caching structure that keeps our application up-to-date
across API results, allows it to store data by entities and keys, and provides us configuration
options to write [manual cache updates](./cache-updates.md) and [local
resolvers](./local-resolvers.md).

While this is all possible without any information about a GraphQL API's schema, the `schema` option
on `cacheExchange` allows us to pass an introspected schema to Graphcache:

```js
const introspectedSchema = {
  __schema: {
    queryType: { name: 'Query' },
    mutationType: { name: 'Mutation' },
    subscriptionType: { name: 'Subscription' },
  },
};

cacheExchange({ schema: introspectedSchema });
```

In GraphQL, [APIs allow for the entire schema to be
"introspected"](https://graphql.org/learn/introspection/), which are special GraphQL queries that
give us information on what the API supports. This information can either be retrieved from a
GraphQL API directly or from the GraphQL.js Schema and contains a list of all types, the types'
fields, scalars, and other information.

In Graphcache we can pass this schema information to enable several features that aren't enabled if
we don't pass any information to this option:

- Fragments will be matched deterministically: A fragment can be written to be on an interface type
  or multiple fragments can be spread for separate union'ed types in a selection set. In many cases,
  if Graphcache doesn't have any schema information then it won't know what possible types a field
  can return and may sometimes make a guess and [issue a
  warning](./errors.md#16-heuristic-fragment-matching). If we pass Graphcache a `schema` then it'll
  be able to match fragments deterministically.
- A schema may have non-default names for its root types; `Query`, `Mutation`, and `Subscription`.
  The names can be changed by passing `schema` information to `cacheExchange` which is important
  if the root type appears elsewhere in the schema, e.g. if the `Query` can be accessed on a
  `Mutation` field's result.
- We may write a lot of configuration for our `cacheExchange` but if we pass a `schema` then it'll
  start checking whether any of the configuration options actually don't exist, maybe because we've
  typo'd them. This is a small detail but can make a large difference in a longer configuration.
- Lastly; a schema contains information on **which fields are optional or required**. When
  Graphcache has a schema it knows optional fields that may be left out, and it'll be able to generate
  "partial results".

### Partial Results

As we navigate an app that uses Graphcache we may be in states where some of our data is already
cached while some aren't. Graphcache normalizes data and stores it in tables for links and records for
each entity, which means that sometimes it can maybe even execute a query against its cache that it
hasn't sent to the API before.

[On the "Local Resolvers" page](./local-resolvers.md#resolving-entities) we've seen how to write
resolvers that resolve entities without having to have seen a link from an API result before. If
Graphcache uses these resolvers and previously cached data we often run into situations where a
"partial result" could already be generated, which is what Graphcache does when it has `schema`
information.

![A "partial result" is an incomplete result of information that Graphcache already had cached
before it sent an API result.](../assets/partial-results.png)

Without a `schema` and information on which fields are optional, Graphcache will consider a "partial
result" as a cache miss. If we don't have all the information for a query then we can't execute
it against the locally cached data after all. However, an API's schema contains information on which
fields are required and optional, and if our apps are typed with this schema and
TypeScript, can't we then use and handle these partial results before a request is sent to the API?

This is the idea behind "Schema Awareness" and "Partial Results". When Graphcache has `schema`
information it may give us partial results [with the `stale` flag
set](../api/core.md#operationresult) while it fetches the full result from the API in the
background. This allows our apps to show some information while more is loading.

## Getting your schema

But how do you get an introspected `schema`? The process of introspecting a schema is running an
introspection query on the GraphQL API, which will give us our `IntrospectionQuery` result. So an
introspection is just another query we can run against our GraphQL APIs or schemas.

As long as `introspection` is turned on and permitted, we can download an introspection schema by
running a normal GraphQL query against the API and save the result in a JSON file.

```js
import { getIntrospectionQuery } from 'graphql';
import fetch from 'node-fetch'; // or your preferred request in Node.js
import * as fs from 'fs';

fetch('http://localhost:3000/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    variables: {},
    query: getIntrospectionQuery({ descriptions: false }),
  }),
})
  .then(result => result.json())
  .then(({ data }) => {
    fs.writeFile('./schema.json', JSON.stringify(data), err => {
      if (err) {
        console.error('Writing failed:', err);
        return;
      }
      console.log('Schema written!');
    });
  });
```

Alternatively, if you're already using [GraphQL Code Generator](https://graphql-code-generator.com/)
you can use [their `@graphql-codegen/introspection`
plugin](https://graphql-code-generator.com/docs/plugins/introspection) to do the same automatically
against a local schema. Furthermore it's also possible to
[`execute`](https://graphql.org/graphql-js/execution/#execute) the introspection query directly
against your `GraphQLSchema`.

## Optimizing a schema

An `IntrospectionQuery` JSON blob from a GraphQL API can without modification become quite large.
The shape of this data is `{ "__schema": ... }` and this _schema_ data will contain information on
all directives, types, input objects, scalars, deprecation, enums, and more. This can quickly add up and one of the
largest schemas, the GitHub GraphQL API's schema, has an introspection size of about 1.1MB, or about
50KB gzipped.

However, we can use the `@urql/introspection` package's `minifyIntrospectionQuery` helper to reduce
the size of this introspection data. This helper strips out information on directives, scalars,
input types, deprecation, enums, and redundant fields to only leave information that _Graphcache_
actually requires.

In the example of the GitHub GraphQL API this reduces the introspected data to around 20kB gzipped,
which is much more acceptable.

### Installation & Setup

First, install the `@urql/introspection` package:

```sh
yarn add @urql/introspection
# or
npm install --save @urql/introspection
```

You'll then need to integrate it into your introspection script or in another place where it can
optimise the introspection data. For this example, we'll just add it to the fetching script from
[above](#getting-your-schema).

```js
import { getIntrospectionQuery } from 'graphql';
import fetch from 'node-fetch'; // or your preferred request in Node.js
import * as fs from 'fs';

import { getIntrospectedSchema, minifyIntrospectionQuery } from '@urql/introspection';

fetch('http://localhost:3000/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    variables: {},
    query: getIntrospectionQuery({ descriptions: false }),
  }),
})
  .then(result => result.json())
  .then(({ data }) => {
    const minified = minifyIntrospectionQuery(getIntrospectedSchema(data));
    fs.writeFileSync('./schema.json', JSON.stringify(minified));
  });
```

The `getIntrospectionSchema ` doesn't only accept `IntrospectionQuery` JSON data as inputs, but also
allows you to pass a JSON string, `GraphQLSchema`, or GraphQL Schema SDL strings. It's a convenience
helper and not needed in the above example.

## Integrating a schema

Once we have a schema that's already saved to a JSON file, we can load it and pass it to the
`cacheExchange`'s `schema` option:

```js
import schema from './schema.json';

const cache = cacheExchange({ schema });
```

It may be worth checking what your bundler or framework does when you import a JSON file. Typically
you can reduce the parsing time by making sure it's turned into a string and parsed using
`JSON.parse`