Interfaces in Ceramic Protocol: Intro Guide

The Ceramic protocol has expanded ComposeDB's capabilities with a new feature called Interfaces (introduced in ComposeDB v0.6.0). Interfaces are important for setting standards in data models.

Interfaces also constitute another step toward real data composability, which is the ability to easily combine and recombine data from various sources to create new datasets or insights.

This article explains Interfaces, why they're useful, and how they're implemented in ComposeDB.

What are Interfaces?

Interfaces are like a blueprint for data models. They define a set of fields that any type or model must include to be compliant with the Interface. This approach ensures that different models, though unique, maintain a level of standardization and interoperability.

In ComposeDB, Interfaces help create a common ground for diverse data models.

For a simplified example, you could have an Interface Vehicle that represents any kind of vehicle in a transportation system.

interface Vehicle {
  id: ID!
  model: String!
  operators: [Person]
  usedIn: [Route]!
}

This means that any type that implements Vehicle must have these specific fields.

Here are some types that might implement Vehicle:

type Car implements Vehicle {
  id: ID!
  model: String!
  operators: [Person]
  usedIn: [Route]!
  passengerCapacity: Int
  vehicleType: String
}
type Airplane implements Vehicle {
  id: ID!
  model: String!
  operators: [Person]
  usedIn: [Route]!
  wingspan: Float
  airline: String
}

You'll notice that both of these types possess all the fields from the Vehicle Interface, but they also feature additional fields: passengerCapacityvehicleTypewingspan, and airline, that are unique to that specific kind of vehicle.

Interfaces are particularly useful when you wish to return an object or a collection of objects, and these objects could represent several different types.

Benefits of Interfaces in ComposeDB

Interfaces in ComposeDB offer several advantages:

  • Standardization: They create a common standard for different models to follow, which is essential for consistency
  • Flexibility: Interfaces allow for the expansion of models by adding new fields, without breaking the established standards. (Note: Interfaces do not extend the existing models, they create new models that implement the same fields and constraints)
  • Efficient data queries: With Interfaces, it's easier to query data across various models, when these models share the same interface. You don't need to query by all the models separately, you can query by a single interface that these models share. This makes data retrieval more efficient.

Interface Implementation Example

An excellent example of Interfaces in action within Ceramic is the verifiable credentials Interface. This Interface example standardizes the structure for decentralized identity systems.

It establishes a standard set of fields for all credential models, ensuring consistency across applications. At the same time, it allows you to add whatever additional fields you need for your own application.

## Define the overarching VC interface that acts agnostic of our proof type
interface VerifiableCredential
  @createModel(description: "A verifiable credential interface")
{
  controller: DID! @documentAccount
  issuer: Issuer!
  context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  credentialSchema: CredentialSchema!
  credentialStatus: CredentialStatus
  issuanceDate: DateTime!
  expirationDate: DateTime
}

## Use the main interface to create a more specific interface - this one is for EIP712
interface VCEIP712Proof implements VerifiableCredential
  @createModel(description: "A verifiable credential interface of type EIP712")
{
  controller: DID! @documentAccount
  issuer: Issuer!
  context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  credentialSchema: CredentialSchema!
  credentialStatus: CredentialStatus
  issuanceDate: DateTime!
  expirationDate: DateTime
  proof: ProofEIP712! # The new field that is not present in the original interface
}

## Define our EIP712 type that uses VerifiableCredential and VCEIP712Proof interfaces and on top of that, adds credentialSubject specific to our use case
type VerifiableCredentialEIP712 implements VerifiableCredential & VCEIP712Proof
  @createModel(accountRelation: LIST, description: "A verifiable credential of type EIP712")
  @createIndex(fields: [{ path: "issuanceDate" }])
  @createIndex(fields: [{ path: "issuer" }]) {
  controller: DID! @documentAccount
  issuer: Issuer!
  context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
  credentialSchema: CredentialSchema!
  credentialStatus: CredentialStatus
  issuanceDate: DateTime!
  expirationDate: DateTime
  proof: ProofEIP712!
  credentialSubject: CredentialSubject! # The new field that is not present in any of the two implemented interfaces
}

This snippet shows how an Interface (VerifiableCredential) is defined and how a specific type (VerifiableCredentialEIP712) implements it. This snippet is from our Verifiable Credentials example app and you can see more on GitHub.

This example of the verifiable credentials Interface standardizes the basic structure for credentials in decentralized identity systems. By doing so, it facilitates various applications in creating, exchanging, and interpreting credentials in a consistent and reliable way.

Conclusion

Interfaces are crucial in the Ceramic protocol for helping decentralized applications work together. As managing data in a decentralized way becomes more common, knowing and using Interfaces will become more important.

Are you currently building a project on Ceramic or want to connect with the core team? Reach us here.