Easy fragment matching with graphql-code-generator for GraphQL Union types
Hey everyone! Since this is my first post on Medium, I would like to introduce myself first. I have been working as a front-end developer since 2008. I worked on many different companies and projects and for the last 2,5 years, I am a “BlueWeb” engineer in KLM. BlueWeb is Air France KLM’s new converged website for customers. We are building the project with Angular and Angular Material. This application is in communication with our APIs through our GraphQL layer. Apollo is the choice of our GraphQL library both in client and server.
The Short Backstory
I was refactoring some code to handle the API errors and warnings. While we were discussing how to approach them Urigo suggested us to look at this video from Sasha Solomon. Please have a look at it, I think it will give you a good idea about how you can categorise your errors and handle those. Basically, she is suggesting to separate the errors as fatal errors and alternative results. Fatal errors won’t change clients behaviour, while alternative errors could affect it.
Assume that you have a query for getting the flight info from your API. Your API can either respond with a flight list or with an error saying “no fares found”. If you follow Sasha’s suggestion you can create a union type like:
union FlightListResponse = FlightInfo | ApiErrors
So, it would be either a FlightInfo
type or ApiError
type. Your types might look like:
union FlightListResponse = FlightInfo | ApiErrorstype FlightInfo {
origin: String
destination: String
flightNumber: String
}type ApiErrors {
errors: [ApiError]
}type ApiError {
code: Int
message: String
}type Query {
getFlightInfo: FlightListResponse
}
And we can query these like below:
query {
getFlightInfo {
... on ApiErrors {
errors {
code
message
}
}
... on FlightInfo {
origin
destination
flightNumber }
}
}
You can have more info about how to resolve these types here. It is fairly easy to implement.
But, there is always a but
However, if you use the query above in your front-end you will end up with warnings in your console saying: “You are using the simple (heuristic) fragment matcher, but your queries contain union or interface types.
”
Luckily it is very well explained by Apollo in their documentation. You need to query the union and interface types from your schema and provide it to your InMemoryCache
with IntrospectionFragmentMatcher
.
I asked Kamil Kisiela if it would be possible to have this functionality in Graphql Code Generator (If you haven’t heard this library yet, you should definitely check it. It helps you to generate code out of your GrahphQL schema and makes your code typesafe on both client and server). Quickly after he added the fragment-matcher plugin to the generator and it is now ready for you to use in version 0.15.0!
Let’s automate that
fragment-matcher plug-in will create you a file(js, ts, json) with the introspection result in it and you can just pass it to your IntrospectionFragmentMatcher
. In your codegen.yml
file you need to configure the plug-in.
schema:
- "http://localhost/graphql"
config: {}
generates:
./src/introspection-result.ts:
plugins:
- "fragment-matcher"
require: []
Don’t forget to install the graphql-codegen-fragment-matcher
via npm or yarn.
After you install your dependencies and configure your .yml
file run:
gql-gen
in your terminal and your introspection file filled with union and interface types (this is fixed in graphql-code-generator version 0.15.2. Before that it was generating the whole introspection output) will be created.
Your Apollo client will look something like this after you import your new introspection results:
import { HttpClientModule } from "@angular/common/http";
import { ApolloModule, APOLLO_OPTIONS } from "apollo-angular";
import { HttpLinkModule, HttpLink } from "apollo-angular-link-http";
import {
InMemoryCache,
IntrospectionFragmentMatcher
} from "apollo-cache-inmemory";// Your generated union and interface types
import introspectionResults from '../introspection-result';const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: introspectionResults
});const cache = new InMemoryCache({
fragmentMatcher
});@NgModule({
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [{
provide: APOLLO_OPTIONS,
useFactory(httpLink: HttpLink) {
return {
cache, // InMemoryCache with custom fragment matcher
link: httpLink.create({
uri: "your_graphql_path"
})
}
},
deps: [HttpLink]
}],
})
export class AppModule {}
Live Demo
You can find the StackBlitz demo here. In the demo, I used apollo-link-schema instead of HttpLink to make it work all in browser.
Thanks
I hope you find this useful. If you have questions you can reach me via Twitter (@ugur_the_human). I would like to thank Kamil Kisiela for his quick action and Mart Ganzevles for his support.