Skip to main content


Define queryable relationships between models and other models or accounts.

Types of Relations

There are a few primary forms of relations currently supported by ComposeDB:

Account to Model

Account to model relations enable linking and querying data to the account that created it. By default the @createmodel directive (used when creating a new model) requires that every model must specify a relation to its author’s account. This was covered in Writing Models.

Example: Simple Profile

Here’s a model for a very simple user profile that can be queried based on the author:

# Define simple profile model
# Relate it to the author's account
# Limit to one profile per account
# Enable queries based on author

type SimpleProfile @createModel(accountRelation: SINGLE, description: "Very basic profile") {
displayName: String! @string(minLength: 3, maxLength: 50)


  • accountRelation relates the profile to the author’s account
  • SINGLE limits to one profile per account

Model to Account

Model to account relations enable you to link data to and query data from an account other than the data’s author. When using this type of relation, you need to define a model field that stores an account (e.g. a DID), then add the @accountReference directive to make it queryable.

Example: Direct message (DM)

Here’s a model for a user-to-user message that can be queried based on the recipient:

# Define message model
# Relate it to author's account
# Allow unlimited sent messages
# Store reference to recipient's account
# Enable queries based on recipient

type Message @createModel(accountRelation: LIST, description: "Direct message model") {
recipient: DID! @accountReference
directMessage: String! @string(minLength: 1, maxLength: 200)


  • accountRelation relates the message to the author’s account
  • LIST allows unlimited messages
  • recipient references the recipient’s account by storing its DID!, using @accountReference

Model to Model

Model to model relations enable you to link data to and query data from another piece of data. These relations can be uni-directional (e.g. query a post from a comment) or bi-directional (e.g. query a post from a comment and query all comments from a post).

There is type of model-to-model relation that includes the user as part of the relationship. This is achieved by using the SET account relation type, which allows users to enforce a constraint where each user account (or DID) can create only one instance of a model for a specific record of another model.

Example: Post with comments and likes

Here’s a model that allows many comments from the same or different account to be made on a single post. It supports unlimited comments per user, and bi-directional queries from any comment or like to the original post and from the original post to all of its comments and likes. The model schema also creates a relation between posts and likes enabling a single like per post by an account, meaning a single account will only be able to like the post once

# Load post model (using streamID)

type Post @loadModel(id: "kjzl6hvfrbw6c99mdfpjx1z3fue7sesgua6gsl1vu97229lq56344zu9bawnf96") {
id: ID!

# New comment model
# Set reference to original post
# Enable querying comment to get original post

type Comment @createModel(accountRelation: LIST, description: "A comment on a Post") {
postID: StreamID! @documentReference(model: "Post")
post: Post! @relationDocument(property: "postID")
text: String! @string(maxLength: 500)

# New like model
# Set relationship to original post
# Enable querying comment to get original post
type Like @createModel(description: "A like on a post", accountRelation: SET, accountRelationFields: ["postID"]) {
postID: StreamID! @documentReference(model: "Post")
post: Post! @relationDocument(property: "postID")

Relations can also be created between models loaded from known streamIDs

# Load comment model

type Comment @loadModel(id: "kjzl6hvfrbw6c9oo2ync09y6z5c9mas9u49lfzcowepuzxmcn3pzztvzd0c7gh0") {
id: ID!

# Load post model
# Extend post model with comments and likes
# Set relationships to all comments and likes
# Enable querying post to get all comments and likes

type Post @loadModel(id: "kjzl6hvfrbw6c99mdfpjx1z3fue7sesgua6gsl1vu97229lq56344zu9bawnf96") {
comments: [Comment] @relationFrom(model: "Comment", property: "postID")
likes: [Like] @relationFrom(model: "Like", property: "postID")


  • id is a simple placeholder, since empty types are not allowed
  • postID defines the relationship from a comment to the original post, using @documentReference
  • post allows accessing the original post from the comment, using @relationDocument
  • text defines a string for the comment
  • comments defines the relationships from a post to a collection of comments, using @relationFrom; requires specifying the model relation (Comment) and the specific property that stores the relation (postID)
  • likes defines the relationships from a post to a collection of comments, using @relationFrom; requires specifying the model relation (Like) and the specific property that stores the relation (postID)

Using interfaces

When defining relations, it is possible to reference model interfaces to allow for a wider range of documents in the relations set, for example to create a collection of documents using different models implementing the same interface:

interface TextContent @createModel(description: "Required text content interface") {
text: String! @string(maxLength: 10000)

type Page implements TextContent @createModel(description: "Page model") {
title: String @string(maxLength: 100)
text: String! @string(maxLength: 10000)

type Post implements TextContent @createModel(description: "Post model") {
title: String! @string(maxLength: 100)
text: String! @string(maxLength: 10000)
createdAt: DateTime!

type ContentCollectionItem
@createModel(description: "Association between a collection and an item") {
# The Node interface is used here instead of ContentCollection, see warning below
collectionID: StreamID! @documentReference(model: "Node")
collection: Node! @relationDocument(property: "collectionID")
itemID: StreamID! @documentReference(model: "TextContent")
item: TextContent! @relationDocument(property: "itemID")

type ContentCollection @createModel(description: "Collection of text contents") {
name: String @string(maxLength: 50)
items: [ContentCollectionItem]!
@relationFrom(model: "ContentCollectionItem", property: "collectionID")
Circular references

ComposeDB does not support creating relations with circular references, such as ContentCollection -> ContentCollectionItem -> ContentCollection in the example above.

To work around this limitation, it is possible to use the Node interface as a placeholder for any model. The example above uses the Node interface instead of ContentCollection to reference the collection in the ContentCollectionItem in order to avoid creating a circular reference.

Account to Account


Account to account relations are on the roadmap, but not yet supported.

Account to account relations enable you to define a relationship between an account and a different account, and query both ways based on that relationship. This is useful for creating structures such as social graphs where the relationship represents a follow.

Next Steps

Now that you understand the fundamentals of creating models with different types of relations, let's create a composite so we can use it in our app.