Omnigraph
Schema

Interfaces

Declare shared properties once and require multiple node types to include them.

An interface declares a set of properties that one or more node types must include. Interfaces keep schemas consistent when several types share the same fields — for example, a common title + embedding pair for anything that needs to be searchable.

Declaring an interface

interface Searchable {
    title:     String @index
    embedding: Vector(1536) @embed(title)
}

An interface looks like a node without storage — it has a name and a list of typed, annotated properties.

Implementing an interface

Use the implements keyword after the node name:

node Article implements Searchable {
    slug:      String @key
    body:      String
}

The node does not need to redeclare every property from the interface. When a property is omitted, the compiler injects it from the interface. If you do redeclare an inherited property locally, its type must match the interface.

For interface properties that carry @key, @unique, or @index, letting the compiler inject them is the clearest way to preserve the interface behavior.

What the compiler checks

RuleExample violation
Unknown interfaceNode implements Searchable, but no such interface exists.
Type mismatchInterface declares title: String, node declares title: I32.
Nullability mismatchInterface declares title: String, node declares title: String?.
Omitted propertyInterface declares title, node omits it. The compiler injects title automatically.
Conflicting overlapTwo interfaces declare the same property name with incompatible types.

Multiple interfaces

A node can implement more than one interface by separating them with commas:

interface Slugged {
    slug: String @key
}

interface Searchable {
    title:     String @index
    embedding: Vector(1536) @embed(title)
}

node Document implements Slugged, Searchable {
    body:      String
}

If you want the inherited properties to stay visually explicit in the node definition, you can still redeclare them:

node Document implements Slugged, Searchable {
    slug:      String @key
    title:     String @index
    embedding: Vector(1536) @embed(title)
    body:      String
}

If two interfaces declare the same property name, the property type must be identical. Conflicting type declarations are a compile error.

Interfaces with @key

An interface can require that implementing types use a particular property as their key:

interface Slugged {
    slug: String @key
}

node Page implements Slugged {
    slug:  String @key
    title: String
}

node Post implements Slugged {
    slug:    String @key
    content: String
}

This guarantees that every type implementing Slugged has a slug key, so queries and load files can address them uniformly.

No runtime concept

Interfaces are strictly compile-time. They do not create storage tables, do not appear in query results, and have no row identity. Their only purpose is to enforce structural consistency across node types during schema compilation.

Full example

A schema using interfaces to standardize searchable, slugged content:

interface Slugged {
    slug: String @key
}

interface Searchable {
    title:     String @index
    embedding: Vector(1536) @embed(title)
}

node Decision implements Slugged, Searchable {
    summary:   String
    status:    enum(proposed, accepted, rejected, superseded)
}

node Signal implements Slugged, Searchable {
    body:      String
    strength:  enum(strong, moderate, weak)
}

node Actor implements Slugged {
    name: String
    role: String
}

All three types share a slug key. Decision and Signal also share the searchable title + embedding pair. If a type implementing Searchable omits one of those properties, the compiler injects it from the interface.

On this page