Normalizes and denormalizes data for GraphQL operations.
The normalize
function writes normalized documents to a normalized Map
and can be used to cache results of GraphQL queries. It traverses the GraphQL AST and only includes the fields specified in the GraphQL Document in the normalized results.
The normalize
function only normalizes entities that include a __typename
field and a valid ID.
IDs are determined by the following:
- If a
TypePolicy
is provided for the given type, it'sTypePolicy.keyFields
are used. - If a
dataIdFromObject
funciton is provided, the result is used. - The
id
or_id
field (respectively) are used.
The library includes the following methods:
normalize
: writes normalized documents to a normalizedMap
denormalize
: reads and reconstructs objects from a normalizedMap
normalizeFragment
: writes normalized fragments to a normalizedMap
denormalizeFragment
: reads and reconstructs fragments from a normalizedMap
Feature | Progress |
---|---|
Fragments | ✅ |
Variables | ✅ |
Interface & Union types | ✅ |
Aliases | ✅ |
TypePolicy s (see Apollo) |
✅ |
Assuming we have the following query...
import 'package:gql/language.dart';
import 'package:gql/ast.dart';
import 'package:normalize/normalize.dart';
final DocumentNode query = parseString("""
query TestQuery {
posts {
id
__typename
author {
id
__typename
name
}
title
comments {
id
__typename
commenter {
id
__typename
name
}
}
}
}
""")
... and executing that query produces the following response data:
final Map<String, dynamic> data = {
"posts": [
{
"id": "123",
"__typename": "Post",
"author": {
"id": "1",
"__typename": "Author",
"name": "Paul",
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"__typename": "Comment",
"commenter": {
"id": "2",
"__typename": "Author",
"name": "Nicole",
}
}
],
}
]
};
We can then run our normalize function:
final normalizedMap = {};
normalizeOperation(
merge: (dataId, value) =>
(normalizedMap[dataId] ??= {}).addAll(value),
document: query,
data: data,
);
print(normalizedMap);
Which will produce the following normalized result:
{
"Query": {
"posts": [
{"$ref": "Post:123"}
]
},
"Post:123": {
"id": "123",
"__typename": "Post",
"author": {"$ref": "Author:1"},
"title": "My awesome blog post",
"comments": [
{"$ref": "Comment:324"}
],
},
"Author:1": {
"id": "1",
"__typename": "Author",
"name": "Paul",
},
"Comment:324": {
"id": "324",
"__typename": "Comment",
"commenter": {"$ref": "Author:2"}
},
"Author:2": {
"id": "2",
"__typename": "Author",
"name": "Nicole",
}
}
If we later want to denormalize this data (for example, when reading from a cache), we can call denormalize
on the normalizedMap from above. This will give us back the original data response object.
denormalizeOperation(
document: query,
read: (dataId) => normalizedMap[dataId],
)
TypePolicy.keyFields
and FieldPolicy.keyArgs
currently only accept a flat list of String
s. Functions and nested lists of strings and are not yet supported. FieldPolicy.merge
and FieldPolicy.read
are also not yet supported.
This library depends on the gql library.