A schema can be provided to the cache so it knows about the shape of the data.
It can be used to define the entities and their relationships, but also any data types.
Types can be defined with factory functions on the schema
object:
import { schema } from "normalized-cache";
const Post = schema.object({
name: "Post",
});
Types can be registered when initializing the cache:
const cache = new Cache({
types: [Post],
});
All types referenced by Post
will be automatically added.
An entity is a type which has a name
and an id
. Data matching these types will be stored normalized.
const Post = schema.object({
name: "Post",
});
cache.write({
type: "Post",
data: { id: "1", title: "Title" },
});
const result = cache.read({
type: "Post",
id: "1",
});
By default the cache will look for an id
property in the data but a custom id
function can also be defined on the type:
const Post = schema.object({
name: "Post",
id: (post) => post.uid,
});
cache.write({
type: "Post",
data: { uid: "1", title: "Title" },
});
const result = cache.read({
type: "Post",
id: "1",
});
An ID can also be specified when writing:
const Post = schema.object({
name: "Post",
});
cache.write({
type: "Post",
id: "1",
data: { title: "Title" },
});
const result = cache.read({
type: "Post",
id: "1",
});
When no ID is provided the entity will be stored as singleton:
const Post = schema.object({
name: "Post",
});
cache.write({
type: "Post",
data: { title: "Title" },
});
const result = cache.read({
type: "Post",
});
Relationships are defined by refering to another type:
const Author = schema.object({
name: "Author",
});
const Comment = schema.object({
name: "Comment",
});
const Post = schema.object({
name: "Post",
fields: {
author: Author,
comments: [Comment],
},
});
Defining a string:
schema.string();
Defining a string constant:
schema.string("DRAFT");
Defining a number:
schema.number();
Defining a number constant:
schema.number(0);
Defining a boolean:
schema.boolean();
Defining a boolean constant:
schema.boolean(true);
Defining an anonymous object:
schema.object();
Defining a named object:
const Post = schema.object({
name: "Post",
});
Defining object fields:
const Post = schema.object({
name: "Post",
fields: {
title: schema.nonNullable(schema.string()),
createdAt: schema.number(),
},
});
Computed fields can be created by defining a read
function.
They can be used for calculations or dynamically mapping fields to entities:
const Author = schema.object({
name: "Author",
});
const Post = schema.object({
name: "Post",
fields: {
author: {
read: (post, { toReference }) => {
return toReference({ type: "Author", id: post.authorId });
},
},
},
});
Defining fields with arguments:
const Author = schema.object({
name: "Author",
});
const Post = schema.object({
name: "Post",
fields: {
author: {
type: Author,
arguments: true,
},
},
});
cache.write({
type: "Post",
data: {
'author({"id":1})': {
id: "1",
name: "Name",
},
},
});
Defining an anonymous array:
schema.array();
Defining an array of a specific type:
const Posts = schema.array(Post);
Defining a union:
const PostOrComment = schema.union({
types: [Post, Comment],
resolveType: (value) => (value?.title ? Post : Comment),
});
The isOfType
property can also be used:
const Post = schema.object({
name: "Post",
isOfType: (value) => value?.title,
});
const Comment = schema.object({
name: "Comment",
isOfType: (value) => value?.text,
});
const SearchResults = schema.array({
name: "SearchResults",
ofType: schema.union([Post, Comment]),
});
Defining non-nullable values:
schema.nonNullable(schema.string());