AIP-124

Resource association

APIs sometimes have resource hierarchies that can not be cleanly expressed in the usual tree structure. For example, a resource may have a many-to-one relationship with two other resource types instead of just one. Alternatively, a resource may have a many-to-many relationship with another resource type.

Guidance

A resource must have at most one canonical parent, and List requests must not require two distinct "parents" to work.

Multiple many-to-one associations

If a resource has a many-to-one relationship with multiple resource types, it must choose at most one of them to be the canonical parent. The resource may be associated with other resources through other fields on the resource.

message Book {
  // The resource name pattern for Book indicates that Publisher is the
  // canonical parent.
  option (google.api.resource) = {
    type: "library.googleapis.com/Book"
    pattern: "publishers/{publisher}/books/{book}"
  };

  // The resource name for the book.
  string name = 1;

  // The resource name for the book's author.
  string author = 2 [(google.api.resource_reference) = {
    type: "library.googleapis.com/Author"
  }];
}

When listing resources with multiple associations in this way, the RPC must treat the string parent field as required as discussed in AIP-132, and must not add additional required arguments. The RPC should include a string filter field that allows users to filter by other resource associations as discussed in AIP-160.

Note: Resource reference fields must accept the same resource name format that is used in the name field of the referenced resource.

Many-to-many associations

Many-to-many associations are less common in APIs than they are in relational databases, in part because they are more difficult to model and present over network interfaces.

An API may contain many-to-many relationships, and should use a repeated field containing a list of resource names, following the principles described for repeated fields in AIP-144.

message Book {
  option (google.api.resource) = {
    type: "library.googleapis.com/Book"
    pattern: "publishers/{publisher}/books/{book}"
  };

  string name = 1;

  // The resource names for the book's authors.
  repeated string authors = 2 [(google.api.resource_reference) = {
    type: "library.googleapis.com/Author"
  }];
}

Note: See AIP-144 for more information on repeated fields, including how to handle common issues such as atomic changes.

If the use of a repeated field is too restrictive, or if more metadata is required along with the association, an API may model a many-to-many relationship using a sub-resource with two one-to-many associations.

message BookAuthor {
  // The resource pattern for BookAuthor indicates that Book is the
  // canonical parent.
  option (google.api.resource) = {
    type: "library.googleapis.com/BookAuthor"
    pattern: "publishers/{publisher}/books/{book}/authors/{book_author}"
  };

  // The resource name for the book-author association.
  string name = 1;

  // The resource name for the author.
  string author = 2 [(google.api.resource_reference) = {
    type: "library.googleapis.com/Author"
  }];

  // Other fields...
}

Note: Using subresources to model an association between resources is only recommended if additional metadata is required in the relationship, or if the restrictions around the use of a repeated field preclude the use of that approach.

Changelog

  • 2021-04-07: Clarified that resource reference fields accept resource names with the same format as the name field of the resource.