In the last cycle I spent quite some time using Apollo client to interact with our GraphQL service from React app. Here are some gotchas and nice features I learnt.
Typing system in GraphQL
I first got to know how custom scalar types in GraphQL work when pairing with Nathan on submitting data to graphQL.
This discovery started when we were trying to submit data to GraphQL that has Int64
type, and we keep getting expected an Int64
while the data we had was of TypeScript number
type. Turned out that Int64
was a custom scala type that our GraphQL microservice defined.
Then we discovered that there are only a limited set of default scalar types in GraphQL:
- Int
- Float
- String
- Boolean
- ID (The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.)
And Int64
is our custom scalar, and its serialization/deserialization rule is defined inside our GraphQL microservice.
Inside schema.graphql
, these custom scala are defined by mapping to model
files:
1 | """ |
I just had a go with what the type really is when I submit a number
:
1 | func UnmarshalInt64(v interface{}) (Int64, error) { |
The console output shows that the input v
is of type json.Number
🤔 And we did not define how this type unmarshals to Int64
scalar.
Solution
If we need to transfer an Int64
, we need to transfer it as a string
.
Although here because we do not need this big of a number, we changed the expected input type on GraphQL side to be Int
.
Lessons learnt
GraphQL:
scalar
is the “minimum”/“leaf” type for GraphQL that is used to build up object- there is a limited set of default scalar, and the others are customer defined. Keep in mind that we do not have 100% control of how the endpoint is called.
- When choosing the type for a GraphQL schema, try to choose the simplest scalar data that does the job
GraphQL Client:
- Do not make assumptions of how the scalar types are used. The custom scalar is available via the GraphiQL docs, good to have a look. e.g.
Use the InMemoryCache
feature of Apollo client to save network round trips on CRUD operations
Three options to make apollo update the cache:
1. If there is a field that has a 1:1 relationship to the data, apollo can do the cache update itself
Got this trick from Greg, the keys are:
- to help apollo find out who the
keyFields
is for this data type:
1 | cache: new InMemoryCache({ |
- make sure the response that updates the data has the correct data structure of this type (i.e. when
delete
, returningdeleted: true
is not good enough, need to return the full data structure withstatus
set toOFF
)
then whenever there are this data type coming in from a response of a useQuery
or useMutation
call, apollo knows where to update it.
2. use cache.modify
to do a “surgical” update on the data
e.g. To delete a field’s value:
1 | cache.modify({ |
3. use cache.readQuery
and cache.writeQuery
to operate on the cache just like we interact with gql server.
Most expressive.
Use standard GraphQL queries.