Produce
This document assumes that you've already followed our installation guide.
While Ceramic will eventually support a diversity of different stream types to assist different use cases, developers using the network today produce and consume Ceramic streams using models and model instance documents.
Models
A model
represents the schema for a single piece of data (e.g. a user profile), which includes the
required and optional field definitions that profile should contain, in addition to traits like
relationships to user accounts and other models. When working with models in Ceramic, developers
create and use model streams that contain the schema definition of the model, in addition to traits
from a base Ceramic stream (such as the account that created the stream, etc.)
Developers can use the @ceramic-sdk/model-client
sub-package to create new model streams, fetch
model definitions, and other utilities needed to create model instance documents that leverage those
definitions.
To get started, use the base CeramicClient
class to instantiate a new ModelClient
class. For
more information regarding usage of the CeramicClient see the previous page.
Add client packages to your project
npm install --save @ceramic-sdk/model-client @ceramic-sdk/model-protocol @ceramic-sdk/model-instance-client @didtools/key-did
Working with Models
import { CeramicClient } from "@ceramic-sdk/http-client";
import { ModelClient } from "@ceramic-sdk/model-client";
import type { ModelDefinition } from "@ceramic-sdk/model-protocol";
import { getAuthenticatedDID } from "@didtools/key-did";
const authenticatedDID = await getAuthenticatedDID(new Uint8Array(32));
const ceramic = new CeramicClient({ url: "http://localhost:5101" });
// 1. Create a model client
const modelClient = new ModelClient({
ceramic,
did: authenticatedDID,
});
// 2. Define the Model's Schema Definition
const model: ModelDefinition = {
version: "2.0",
name: "Profile",
description: "A simple profile",
accountRelation: { type: "single" },
interface: false,
implements: [],
schema: {
type: "object",
properties: {
firstName: { type: "string", maxLength: 12 },
lastName: { type: "string", maxLength: 12 },
userName: { type: "string", maxLength: 12 },
},
required: ["userName"],
additionalProperties: false,
},
};
// 3. Create the Model Stream
const modelStream = await modelClient.createDefinition(model);
// 4. Get the Stream's Model Definition
const modelDefinition = await modelClient.getModelDefinition(modelStream);
Model Types
There are three types of models Ceramic developers use that are differentiated based on the relationship they define to the account that is using them to create model instance documents:
single
A model that defines a single
accountRelation ensures that each Ceramic user can create only one
model instance document using that model definition. This is commonly used for profiles or user
information documents for which applications want to ensure that a user does not intentionally or
unintentionally create more than one instance, resulting in the burden of additional application
logic that would otherwise need to be handled by the application layer. For example:
const singleModel: ModelDefinition = {
version: "2.0",
name: "Profile",
description: "A simple profile",
accountRelation: { type: "single" },
interface: false,
implements: [],
schema: {
type: "object",
properties: {
firstName: { type: "string", maxLength: 12 },
lastName: { type: "string", maxLength: 12 },
userName: { type: "string", maxLength: 12 },
},
required: ["userName"],
additionalProperties: false,
},
};
set
A model that defines a set
accountRelation ensures that each Ceramic user can create a unique set
of model instance documents based on the corresponding field(s) that define the set constraint in
the schema. For example, a marketplace application might want to allow users to leave reviews for
products they've purchased, and would want to ensure that each user can only leave 1 review per
product. This allows users to leave reviews for as many products as they wish, but no more than 1
per product. For example:
const setModel: ModelDefinition = {
version: "2.0",
name: "Review",
description: "A product review",
accountRelation: {
type: "set",
fields: ["productId"],
},
schema: {
type: "object",
$schema: "https://json-schema.org/draft/2020-12/schema",
properties: {
rating: {
type: "number",
},
productId: {
type: "number",
},
reason: {
type: "string",
},
},
required: ["productId", "rating"],
additionalProperties: false,
},
interface: false,
implements: [],
};
In the example above, the set
relation is defined on the "productId" field, which would ensure
that each user could only create one model instance document per unique productId.
list
A model that defines a list
accountRelation allows controlling accounts to create as many model
instance documents of that model as they want. For example, a forum application might want to allow
users to create as many posts as they'd like:
const listModel: ModelDefinition = {
version: "2.0",
name: "Post",
description: "A forum post",
accountRelation: { type: "list" },
interface: false,
implements: [],
schema: {
type: "object",
properties: {
title: { type: "string", maxLength: 100 },
body: { type: "string", maxLength: 1000 },
},
additionalProperties: false,
},
};
Model Instance Documents
A model instance document
(or MID) stream uses the definition of a model to create a Ceramic
stream containing content that adheres to the model's schema definition. Developers can use the
Ceramic SDK to create new MIDs, fetch the current state from an MID, and update MIDs by leveraging
the @ceramic-sdk/model-instance-client
sub-package.
Working with MIDs
import { CeramicClient } from "@ceramic-sdk/http-client";
import { ModelInstanceClient } from "@ceramic-sdk/model-instance-client";
import { getAuthenticatedDID } from "@didtools/key-did";
import { StreamID } from "@ceramic-sdk/identifiers";
const authenticatedDID = await getAuthenticatedDID(new Uint8Array(32));
const ceramic = new CeramicClient({ url: "http://localhost:5101" });
// a model using the single accountRelation
const profileModelStreamId = StreamID.fromString("kjzl6hvfrbw6c77ooylhtrs71a3ddqi5zdzosmvaodxu1upnhl6t8x6ekf5k2t8");
// a model using the list accountRelation
const forumPostModelStreamId = StreamID.fromString("kjzl6hvfrbw6c8007xlqeudoh0n82j0fepj4y2in7uskh74yv3612d3bi7y7p1j");
// 1. Instantiate a ModelInstanceClient
const modelInstanceClient = new ModelInstanceClient({
ceramic,
did: authenticatedDID,
});
// 2. Create a new MID (single` and `set` model types are handled differently than `list` types)
// `single` and `set` require a deterministic initial event (without content)
const singleDocumentStream = await modelInstanceClient.createSingleton({
model: profileModelStreamId,
controller: authenticatedDID.id,
});
// `list` documents can contain an initial content payload
const listDocumentStream = await modelInstanceClient.createInstance({
model: forumPostModelStreamId,
content: {
title: "This is a new post",
body: "This is the body for a new post",
},
shouldIndex: true,
});
// 3. Updating a MID (does not differ between model types)
await modelInstanceClient.updateDocument({
streamID: singleDocumentStream.baseID.toString(),
newContent: { firstName: "New", lastName: "User", userName: "newUser1" },
shouldIndex: true,
});
await modelInstanceClient.updateDocument({
streamID: listDocumentStream.baseID.toString(),
newContent: {
title: "This is an updated post title",
body: "This is an updated post body",
},
shouldIndex: true,
});
// 4. Read the MID's State
const currentState1 = await modelInstanceClient.getDocumentState(singleDocumentStream.baseID);
const currentState2 = await modelInstanceClient.getDocumentState(listDocumentStream.baseID);