Relationships

Relationships describe how documents in different collections are associated, so that related documents can be read, updated, or deleted together. Entities in real-life often associate with each other in an organic and logical way, like a person and their dog, an album and its songs, or friends in a social network.

These types of association between entities can be modeled in Appwrite using relationships.

Experimental feature

Appwrite Relationships is an experimental feature. The API and behavior are subject to change in future versions.

Relationship Attributes

Relationships are represented in a collection using relationship attributes. The relationship attribute contains the ID of related documents, which it references during read, update, and delete operations. This attribute is null if a document has no related documents.

When to use a relationship

Relationships help reduce redundant information. For example, a user can create many posts in your app. You can model this without relationships by keeping a copy of the user's information in all the documents representing posts, but this creates a lot of duplicate information in your database about the user.

Benefits of relationships

Duplicated records waste storage, but more importantly, makes the database much harder to maintain. If the user changes their user name, you will have to update dozens or hundreds of records, a problem commonly known as an update anomaly in databases. You can avoid duplicate information by storing users and posts in separate collections and relating a user and their posts through a relationship.

Tradeoff

Consider using relationships when the same information is found in multiple places to avoid duplicates. However, relationships come with the tradeoff of slowing down queries. For applications where the best read and write performance is important, it may be acceptable to tolerate duplicate data.

Directionality

Appwrite relationships can be one-way or two-way.

TypeDescription
One-wayThe relationship is only visible to one side of the relation. This is similar to a tree data structure.
Two-wayThe relationship is visible to both sides of the relationship. This is similar to a graph data structure.

Types

Appwrite provides four different relationship types to enforce different associative rules between documents.

TypeDescription
One-to-oneA document can only be related to one and only one document.
One-to-manyA document can be related to many other documents.
Many-to-oneMany documents can be related to a single document.
Many-to-manyA document can be related to many other documents.

On-delete

Appwrite also allows you to define the behavior of a relationship when a document is deleted.

TypeDescription
RestrictIf a document has at least one related document, it cannot be deleted.
CascadeIf a document has related documents, when it is deleted, the related documents are also deleted.
Set nullIf a document has related documents, when it is deleted, the related documents are kept with their relationship attribute set to null.

Creating relationships

You can define relationships in the Appwrite Console, or using a Server SDK

    Creating documents

    If a collection has relationship attributes, you can create documents in two ways. You create both parent and child at the same time using a nested syntax or link parent and child documents through references*.

      Queries

      Queries are currently not available in the experimental version of Appwrite Relationships but will be added in a later version.

      Update Relationships

      Relationships can be updated by updating the relationship attribute.

      const { Client, Databases } = require('node-appwrite');
      
      const client = new Client()
          .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
          .setProject('<PROJECT_ID>');               // Your project ID
      
      const databases = new Databases(client);
      
      await databases.updateDocument(
          'marvel',
          'movies',
          'spiderman',
          {
              title: 'Spiderman',
              year: 2002,
              reviews: [
                  'review4',
                  'review5'
              ]
          }
      );
      

      Delete relationships

      If you need to unlink documents in a relationship but retain the documents, you can do this by updating the relationship attribute and removing the ID of the related document.

      If a document can be related to only one document, you can delete the relationship by setting the relationship attribute to null.

      If a document can be related to more than one document, you can delete the relationship by setting the relationship attribute to an empty list.

      Delete relationships and documents

      If you need to delete the documents as well as unlink the relationship, the approach depends on the on-delete behavior of a relationship.

      If the on-delete behavior is restrict, the link between the documents needs to be deleted first before the documents can be deleted individually.

      If the on-delete behavior is set null, deleting a document will leave related documents in place with their relationship attribute set to null. If you wish to also delete related documents, they must be deleted individually.

      If the on-delete behavior is cascade, deleting the parent documents also deletes related child documents, except for many-to-one relationships. In many-to-one relationships, there are multiple parent documents related to a single child document, and when the child document is deleted, the parents are deleted in cascade.

      const { Client, Databases } = require('node-appwrite');
      
      const client = new Client()
          .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
          .setProject('<PROJECT_ID>');               // Your project ID
      
      const databases = new Databases(client);
      
      await databases.deleteDocument(
          'marvel',
          'movies',
          'spiderman'
      );
      

      Permissions

      To access documents in a relationship, you must have permission to access both the parent and child documents.

      When creating both the parent and child documents, the child document will inherit permissions from its parent.

      You can also provide explicit permissions to the child document if they should be different from their parent.

      const { Client, Databases, ID } = require('node-appwrite');
      
      const client = new Client()
          .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
          .setProject('<PROJECT_ID>');               // Your project ID
      
      const databases = new Databases(client);
      
      await databases.createDocument(
          'marvel',
          'movies',
          ID.unique(),
          {
              title: 'Spiderman',
              year: 2002,
              reviews: [
                  { 
                      author: 'Bob', 
                      text: 'Great movie!',
                      $permissions: [
                          Permission.read(Role.any())
                      ]
                  },
              ]
          }
      );
      

      When creating, updating, or deleting in a relationship, you must have permission to access all documents referenced. If the user does not have read permission to any document, an exception will be thrown.

      Limitations

      Relationships can be nested between collections, but are restricted to a max depth of three levels. Relationship attribute key, type, and directionality can't be updated. On-delete behavior is the only option that can be updated for relationship attributes.