> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data modeling language extension for GraphQL

> Learn about the Data modeling language extension for GraphQL (DML) for managing data models and views.

> For the complete Cognite documentation index, see [/llms.txt](/llms.txt). For full page content, see [/llms-full.txt](/llms-full.txt).

The Data Modeling Language extension (DML) offers a simplified way of building data models in CDF based on the [GraphQL Schema Definition Language](https://graphql.org/learn/schema).

A **data model** in the context of DML is a collection of related **types** representing the domain specific concepts you want to model for a given use-case.

DML functions as an abstraction layer on top of the Data Modeling Service (DMS) API, when you define a data model using DML and publish it, the types and properties in your DML data model are converted into the relevant data models, views, and containers in DMS.

<Note>
  The [**data modeling REST API (DMS)**](/api-reference/concepts/20230101/data-models) in combination with the [Cognite Python SDK](/dev/sdks/python) offers more power and flexibility than the DML extension. They're the preferred tools for managing industrial knowledge graphs in Cognite Data Fusion (CDF).
</Note>

## DML HTTP endpoint

Send **`POST`** requests with a JSON GraphQL payload to:

```
https://{cluster}.cognitedata.com/api/v1/projects/{project}/dml/graphql
```

See [Endpoint](/cdf/dm/dm_graphql/dm_graphql_querying#endpoint) in *GraphQL queries* for `{cluster}`, `{project}`, and authentication. Use this `/dml/graphql` URL for DML schema work only. For instance queries and mutations, use the GraphQL URL for your data model on that same page.

## Types

**Types** are the basic building block of your data model and represent use-case specific concepts. Each type is associated with one or more **properties** which are each associated with another type.

Below is a simple type definition in DML modeling an Equipment type with two properties: **title** and **pages**:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Equipment {
  title: String
  pages: Int
}
```

### Extending types via inheritance

Using GraphQL inheritance rules, you can create types that extend parent types and inherit all their properties.

* Define a type as an **interface** to make it extendable.
* Use the `implements` keyword to extend this base type from another type.
* Extend multiple interfaces by separating them with an ampersand (`&`).

<Note>
  In most implementations of the GraphQL Schema Definition Language, interfaces are treated differently from types; they cannot be queried directly and serve only as blueprints to be implemented by other types.

  In DML, interfaces function as both types and interfaces. This means that when you change a type to an interface, the only functional difference is that it can now serve as a parent type for others. You can still query the interface via the CDF APIs as if it were still a standalone type in your data model.
</Note>

Below is an example of inheritance in DML:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
interface Equipment {
  name: String
  installationDate: Timestamp
}
type Pipe implements Equipment {
  name: String
  installationDate: Timestamp
  diameter: Float
  length: Float
}
```

In this example, the parent type (`Equipment`) is implemented by the `Pipe` type, which inherits all parent properties and adds specific properties relevant only to the child type.

There are some caveats with fields that are imported from inheritance, learn more in the [advanced section](#advanced-mapping-and-importing-fields).

## Properties

A type consists of a collection of properties. Each property can be a `scalar`, an `enum`, or a reference to a `custom type`. When a property references a custom type, it represents a `relation` between instances in your data model.

### List properties

By default a property can only contain a single value:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Book {
  # A single book can only have a single tag
  tag: String
}
```

To make a list property that can contain multiple values, add brackets (`[]`) around the type of the property:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Book {
  # A single book can have multiple tags
  tag: [String]
}
```

#### Limiting number of values on list properties

By default list properties can have maximum of between 100 and 1000 values [depending on the property type](/cdf/dm/dm_reference/dm_limits_and_restrictions#property-value-limits).

To override the default maximum values on a list property, use the `@limits` directive:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Book {
  # A single book can have up to 3 tags
  tag: [String] @limits(maxListSize: 3)
}
```

### Scalar properties

Scalar properties represent primitive data.

| DML type name        | DMS name  | Description                                |
| -------------------- | --------- | ------------------------------------------ |
| `String`             | text      |                                            |
| `Int` or `Int32`     | int32     | 32-bit integer number                      |
| `Int64`              | int64     | 64-bit integer number                      |
| `Float` or `Float64` | float64   | 64-bit number encoded in IEEE 754          |
| `Float32`            | float32   | 32-bit number encoded in IEEE 754          |
| `Timestamp`          | timestamp | Timestamp encoded in ISO 8601              |
| `JSONObject`         | json      | JSON encoded data                          |
| `Date`               | date      | Date encoded in ISO 8601, but without time |
| `Boolean`            | boolean   |                                            |

### Enum properties

Enum properties are special scalars that must match one of several predefined, distinct values. To create one, declare an enum and then use that enum as the type of another property:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
enum StatusEnum {
  ACTIVE
  INACTIVE
}

type Equipment {
  name: String!
  status: StatusEnum
}
```

### CDF resource type properties

Properties can also reference non-data-modeling resource types in CDF. The value of these properties is a string reference to the external ID of the underlying resource.

| GraphQL type | CDF resource                                                                       |
| ------------ | ---------------------------------------------------------------------------------- |
| File         | [files](https://developer.cognite.com/dev/concepts/resource_types/files)           |
| TimeSeries   | [timeseries](https://developer.cognite.com/dev/concepts/resource_types/timeseries) |
| Sequence     | [sequences](https://developer.cognite.com/dev/concepts/resource_types/sequences)   |

Using these native types enables specialized querying capabilities via the GraphQL API and some of our SDKs.

Below is an example of a type having CDF native resource properties:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Equipment {
  name: String
  documentation: File
  temperature: TimeSeries
  voltage: TimeSeries
  performanceCurve: Sequence
}
```

### Relation properties

**Relations** allow types to reference other types, forming connections within your data model. These can point to single or multiple target instances.

You create relation properties in DML by defining a property that uses a custom type as its type:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Author {
  name: String
  birthDate: Timestamp
}

type Book {
  name: String
  isbn: String
  author: Author
}
```

This example models a type representing a book with a relation connecting it to a single author.

In DMS there are three types of relation properties:

1. **Direct relations:** A container property storing a reference to another node.
2. **Edges:** A connection property that can be used to describe one-to-many or many-to-many relationships.
3. **Reverse direct relation:** A connection property representing a direct relation in another type going in the opposite direction.

You can read more about these in the [direct relations vs. edges](/cdf/dm/dm_concepts/dm_spaces_instances#direct-relations-vs-edges) and the [reverse direct relations](/cdf/dm/dm_concepts/dm_containers_views_datamodels#reverse-direct-relation) sections in the data modeling documentation.

The type of relation property used for a property in DML is determined by several factors described below.

#### Direct relations

A relation with a single target instance defaults to a **direct relation**:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # This will become a direct relation property in DMS
    documentation: Document
}
```

To model a **list** of references using direct relations, you must add the `@directRelation` directive. Without this, list property will default to being modelled as an **edge**:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # This will become a direct relation property in DMS
    documentation: [Document] @directRelation
}
```

#### Reverse direct relations

A **reverse direct relation** looks "backward" through an existing direct relation on the target type. You must specify the `throughProperty` on the target type for the graph traversal.

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Document {
    # The "throughProperty" must match the name of the direct relation on the target type
    referencingAssets: Asset @reverseDirectRelation(throughProperty: "documentation")
}

type Asset {
    documentation: Document
}
```

#### Edges

Since single-target relations default to direct relations, add the `@relation` directive to define a single-target edge relation:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # This will become an edge connection property in DMS
    documentation: Document @relation
}
```

A multiple target relation property defaults to edge so in this case no `@relation` directive is necessary to model it as an edge:

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # This will become an edge connection property in DMS
    documentation: [Document]
}
```

##### Properties on edges

Edges can possess their own properties. To define these on an edge relation, create a type annotated with the `@edge` directive and link it using `edgeSource` in the `@relation` directive:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
# The @edge directive specifies that this type represents the properties of an edge relation property
type PowerCable @edge {
    ratedVoltage: Float
    installationDate: Timestamp
}

type Asset {
    # Represents a connection between two assets via a power cable edge that has its own properties
    connectedTo: Asset @relation(edgeSource: "PowerCable")
}
```

##### Overriding edge type

The **edge type** used to identify an instance of an edge defaults to `{your_typename}.{your_fieldname}`. However, you can override this behavior and manually specify the edge type using the `type` field within the `@relation` directive.

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # Instances of this edge will be identified by the `power_cable` node in the `my_types` space
    connectedTo: Asset @relation(type: { externalId: "power_cable", space: "my_types" })
}
```

##### Overriding edge direction

The relational direction for edges defined in DML is **OUTWARDS** by default. To create a relation that points inwards (referencing incoming edges), add the `direction` argument to `INWARDS` on the `@relation` directive.

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Asset {
    # All power connections from this asset to another asset (Default: OUTWARDS)
    connectedTo: Asset @relation(type: { externalId: "power_cable", space: "type_space" }, direction: OUTWARDS)
    
    # All power connections to this asset from some other asset
    connectedFrom: Asset @relation(type: { externalId: "power_cable", space: "type_space" }, direction: INWARDS)
}
```

### Descriptions

GraphQL descriptions are supported and will be converted to the corresponding description property on view, container and property definitions.

To set the `name` of a view, container, or `field`, start a line in the description with `@name` followed by the name value.

Example of GraphQL descriptions:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
"""
This is a common interface for common models
@name Common View
"""
interface Common {
  name: String
}
type Equipment implements Common {
  """
  This is the description for the property Equipment.name
  @name Name
  """
  name: String
}
```

## Specification of directives

The following is a list of the directive definitions, with descriptions.

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
"""
Specifies that a type is a view type.

* space: Overrides the space, which by default is the same as the data model.
* version: Overrides the version of the view, which by default is the same as the data model version.
* rawFilter: Sets a filter on the view, accepts a JSON string.

"""
directive @view(
  space: String
  version: String
  rawFilter: JSON
) on OBJECT | INTERFACE

"""
Specifies that a type is an edge type.
The view can then be used to expand an edge node with a node instance with properties as specified in the view with the @edge directive.
The edge should be annotated with `@relation(edgeSource: "name_of_edge_view")`.
"""
directive @edge ON OBJECT | INTERFACE

"""
Specifies that a type is importing another view and its backing container.
Currently, this annotation must be combined with @view to specify the view and view version to import. In order to extend the view in your data
model, specify it as an interface and then create a new data type that implements the interface and adds additional properties.
In the future, this directive will be extended to simplify importing.
"""
directive @import ON OBJECT | INTERFACE

"""
Overrides the mapping of a field. Can only be used in a view type and can not be used on imported fields.

* space: Overrides the space, which by default is the same as the data model space.
* container: Overrides the container externalId, which by default is the same as the externalId of the view postfixed with 'Container'.
* property: Overrides the container property identifier being mapped.
"""
directive @mapping(
  space: String
  container: String
  property: String
) on FIELD_DEFINITION

"""
Defines the relation field's details

* type: The reference to the edge type this relation is mapped to.
* name: Overrides the name property of the relation definition. This is merely metadata, and should not be confused with the
  property identifier!
* edgeSource: The name of the edge view to use for expanding the edge node with a node instance.
* direction: The direction to follow the edges filtered by 'type'.
* type: Specifies the edge type, namespaced by 'space', where the 'externalId' corresponds to the edge type name.
"""
directive @relation(
  type: _DirectRelationRef
  name: String
  edgeSource: String
  direction: _RelationDirection
) on FIELD_DEFINITION

"""
Configures the backing container of the view.
It can be used to configure indexes or constraints for the properties of a container.
"""
directive @container(
  constraints: [_ConstraintDefinition!]
  indexes: [_IndexDefinition!]
) on OBJECT | INTERFACE

"""
Sets the default value of a field.

* value: Specifies the default value. The input is always in string format, however all dms supported default value types are also supported.

"""
directive @default(
  value: String
) on FIELD_DEFINITION

"""
Sets the unit on a field.

* externalId: specifies a unit by using an external ID value. `externalId` can only be assigned to the types `Float`, `Float32`, `Float64`, or to an array of listed floating types and must match a unit in the unit catalog.
* sourceUnit: a free text field to store arbitrary unit information like unit name in a source system.

"""
directive @unit(
  externalId: String,
  sourceUnit: String
) on FIELD_DEFINITION

"""
Represents an index definition.
* identifier: A unique identifier for the index
* indexType: Is the type of index
* fields: List of field names to define the index across. Only supported for non-inherited or mapped fields. The order of the fields matters.
* cursorable: With cursorable index we can efficiently query with custom sort options, and queries will emit cursors that can be used to paginate through the results. Only supported for BTREE indexType.
"""
input _IndexDefinition {
  identifier: String!
  indexType: _IndexType
  fields: [String!]!
  cursorable: Boolean
}

"""
Represents a constraint definition. The constraint definition can either be an uniqueness constraint or requires.
In case 'constraintType' is set to 'REQUIRES', the 'require' argument must be set.
And if it's set to 'UNIQUENESS', the 'fields' argument must be set.

* identifier: A unique identifier for the constraint
* constraintType: Is the type of constraint
* fields: List of field names to define the unique constraint across. Only supported for non-inherited or mapped fields. The order of the fields matters.
* require: Specify a container (by space and externalId), which is required to exist on the node, if the current container is to be used.
"""
input _ConstraintDefinition {
  identifier: String!
  constraintType: _ConstraintType
  require: _DirectRelationRef
  fields: [String!]
}

"""
Overrides the constraints on the possible values of a property

* maxTextSize: If set overrides the max number of characters allowed on a string property
* maxListSize: If set overrides the max number of values on a list property
"""
directive @limits(
  maxTextSize: Int,
  maxListSize: Int
) on FIELD_DEFINITION

enum _ConstraintType {
  UNIQUENESS
  REQUIRES
}

enum _IndexType {
  BTREE
}

input _DirectRelationRef {
  space: String!
  externalId: String!
}

enum _RelationDirection {
  INWARDS
  OUTWARDS
}
```

## Advanced: Types and their relation to DMS views and containers

Types in the DML data model are abstractions of DMS schema resources (views, containers, and data models).

When you publish a DML data model in your CDF project, it's persisted in DMS as a collection of schema objects (containers, views, and data models). Similarly, when you open an existing data model in DML, the DML schema is regenerated from the published DMS schema objects.

### Containers and views

Each type in a DML data model has an equivalent view and container. The identifiers for these resources are determined by the following rules:

**Container identification** (by space and external ID):

* **External ID**: Uses the `@container` directive's external ID if present, otherwise defaults to the type name.
* **Space**: Uses the `@container` directive's space if present, otherwise defaults to the data model's space.

**View identification** (by space, external ID, and version):

* **External ID**: Uses the `@view` directive's external ID if present, otherwise defaults to the type name.
* **Space**: Uses the `@view` directive's space if present, otherwise defaults to the data model's space.
* **Version**: Uses the `@view` directive's version if present, otherwise generates a version identifier based on the type definition (ensuring any changes to the type definition result in a new view version).

### View and container properties

Once a type has been mapped to a view and container, its properties are also mapped to equivalent view and container properties.

Each type property always has an equivalent view property in the view mapped to the type. However, which container property the view property maps to is determined by the following rules (in order of precedence):

1. **Explicit mapping**: If the type property has a `@mapping` directive, the directive determines which container property to use.
2. **Inherited mapping**: If the property is inherited from a parent type, the view property maps to the same container property as the equivalent property in the parent type.
3. **Default mapping**: Otherwise, the view property maps to a container property with the same name in the container mapped to the type.

<Note>
  When you use the `@mapping` directive, the system doesn't auto-create the referenced container property if it doesn't exist. Only use `@mapping` to reference container properties that already exist.
</Note>

### Example DML to DMS mapping

```graphql theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
interface Equipment {
  name: String
}

type Valve implements Equipment @view(externalId: "Valve2") {
  name: String
  diameter: Number
  material: String
}
```

When you publish this DML data model, it creates the following DMS resources:

**Equipment interface:**

* **View**: Created with external ID `Equipment` in the data model's space (no `@view` directive, so it uses the type name)
* **Container**: Created with external ID `Equipment` in the data model's space (no `@container` directive, so it uses the type name)
* **Property `name`**:
  * View property `name` in the `Equipment` view
  * Maps to container property `name` in the `Equipment` container (no `@mapping` directive and not inherited, so a new container property is created with the same name)

**Valve type:**

* **View**: Created with external ID `Valve2` in the data model's space (explicitly set via `@view` directive)
* **Container**: Created with external ID `Valve` in the data model's space (no `@container` directive, so it uses the type name)
* **Property `name`**:
  * View property `name` in the `Valve2` view
  * Maps to container property `name` in the `Equipment` container (inherited from the parent `Equipment` interface, so it reuses the same container property as the parent)
* **Property `diameter`**:
  * View property `diameter` in the `Valve2` view
  * Maps to container property `diameter` in the `Valve` container (no `@mapping` directive and not inherited, so a new container property is created)
* **Property `material`**:
  * View property `material` in the `Valve2` view
  * Maps to container property `material` in the `Valve` container (no `@mapping` directive and not inherited, so a new container property is created)

<Note>
  The `Valve` type's `name` property references the `Equipment` container rather than its own `Valve` container. This is because inherited properties always map to the same container property as their parent type.
</Note>

## Spaces and naming collisions

When you create multiple data models in **the same space** with identical type names, they will interfere with each other and cause errors.

Consider this scenario where you have two data models in the same space:

**Data model A:**

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Foo {
    bar: String!
}
```

**Data model B (causes collision):**

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
type Foo {
    foo: String
}
```

When you try to publish data model B, you'll see an error like this:

```graphql wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
Error: could not update data model
An error has occurred. Data model was not published.

[Line: 28] The view (space: 'test', externalId: 'Foo', version: '9317f4591a0e6c') is not mapping a required property 'bar' of the referenced container (space: 'test', externalId: 'Foo').
This error is likely caused by deleting a field which was non-nullable. Please add this field back or add '@readonly' to 'Foo'
```

This happens because these two data models will have view properties that map to the same container property but with different requiredness.

To prevent naming conflicts:

* **Use one data model per space** - This eliminates the possibility of collisions.
* **Use unique type names** - If you need multiple data models in the same space, ensure all type names are unique across models.
