Angular
Caching Graphql Schema for Introspection Fragment Matching
Creating schemaQuery.ts file
Create a file named schemaQuery.ts in the src folder of your application and paste the code below.
if (!process.env.APP_ENVIRONMENT) {
require('dotenv').config();
}
const fs = require('fs');
const fetchReq = require('node-fetch');
const env = process.env.APP_ENVIRONMENT;
let apiUrl = <<Staging URL>>;
if (env === 'prod') {
apiUrl = <<Prod URL>>';
}
fetchReq(`${apiUrl}/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
variables: {},
query: `
{
__schema {
types {
kind
name
possibleTypes {
name
}
}
}
}
`,
}),
})
.then(result => result.json())
.then(result => {
// here we're filtering out any type information unrelated to unions or interfaces
const filteredData = result.data.__schema.types.filter(
type => type.possibleTypes !== null,
);
result.data.__schema.types = filteredData;
fs.writeFileSync('./src/assets/fragmentTypes.json', JSON.stringify(result.data), err => {
if (err) {
console.error('Error writing fragmentTypes file', err);
} else {
console.log('Fragment types successfully extracted!');
}
});
}, err => {
console.error('Error fetching request', err);
});
- For projects that require an access_token to fetch the schema query, you may need to get it from the .env file. Here, we’re just taking the environment from the
.env
file.
The above code will fetch the schema, filter out the possible union or interface types and write it onto a file called fragmentTypes.json in the assets folder.
Update package.json file
After creating the schemaQuery.ts file, go to the package.json
file of your frontend app and update the script to run the schemaQuery.ts file during build time.
"scripts": {
"build": "npm run build-fragment && ng build --configuration=production",
"build-fragment": "node src/schemaQuery.ts"
}
- Always ensure the
build-fragment
command runs before the build command.
Update Graphql Service
Now, update the graphql.service.ts
file or its equivalent file handling the Apollo Initialization, to query the cached schema in fragmentTypes.json for Introspection Fragment Matching.
buildFragmentMatcher(): Promise<any> {
return new Promise(async (resolve, reject) => {
let fragmentMatcher;
try {
this.schema = await this.fetchSchema();
if (!this.schema) {
return reject(new Error('Schema not found'));
}
fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: this.schema
});
return resolve(fragmentMatcher);
} catch (e) {
return reject();
}
});
}
async fetchSchema() {
return new Promise((resolve, reject) => {
this.http.get('./assets/fragmentTypes.json')
.subscribe(async res => {
const response = await res;
resolve(response);
}, async error => {
console.log(error);
console.log('Fetching schema again..');
const res = await this.fetchSchemaAtRuntime().catch(e => {
reject(null);
});
if (res) {
resolve(res);
}
reject(null);
});
});
}
fetchSchemaAtRuntime() {
const token = JSON.parse(this.cookies.get('access-token') || null);
let uri;
if (token && token.access_token) {
uri = `${this.environment.api}/graphql?access_token=${token.access_token}`;
} else {
uri = `${this.environment.api}/graphql`;
}
uri = `${this.environment.api}/graphql`;
const apollo = new ApolloClient({
cache: new InMemoryCache,
link: this.httpLink.create({ uri })
});
const query = gql`
{
__schema {
types {
kind
name
possibleTypes {
name
}
}
}
}
`;
return new Promise((resolve, reject) => {
apollo.query({
query,
variables: {}
})
.then((res: any) => {
// here we're filtering out any type information unrelated to unions or interfaces
const filteredData = res.data.__schema.types.filter(
type => type.possibleTypes !== null
);
const result = JSON.parse(JSON.stringify(res.data));
result.__schema.types = filteredData;
resolve(result);
}, err => {
reject(err);
});
});
}
- Here we updated the
fetchSchema()
method to fetch from the cache first. - In case the fragmentTypes.json does not exist or has any exceptions while sending the initial request, we’ll try sending the request again at runtime through
fetchSchemaAtRuntime()
method.
Add fragmentTypes.json to Git Ignore
Finally, we add the fragmentTypes.json
file in Git Ignore.
src/assets/fragmentTypes.json
Errors
In case there are any errors pertaining to node-fetch, do an npm install
of that package.
Update Travis and AWS (If .env has been updated)
Update the environment variables in Travis and AWS if there were any changes made to the .env
file.