Skip to content

Define the data model

Your connector must specify the shape of the data stored in your data source type by defining a data model using model(). Model definitions are used by Netlify to build a GraphQL schema for your data source, which includes the types and relationships of your data.

The data model should include document models for each type of record stored in your database, and the fields and types stored on each one. The following sections outline the properties available for document models and their fields.

Using Connect and need access to data in real time? Or need data from another API or database?

The following documentation outlines how to build a static connector, which syncs data from a data source and stores it in the data layer database. If you need to build a connector for Connect that requires access to data in real time, you may want to build a dynamic connector instead.

Define document models

You can think of a document as a single unit of information from your data source, such as a post, article, user, product, etc. For each type of information in your data model, you need to define a document model that describes the shape of that data. To define a document model, use define.document().

It’s important to define anything that you want to store, uniquely identify, and query later in a list or by ID as a document model.

A document model includes the following properties:

  • id: defined by default, this is the unique ID within your document model type. You don’t need to define an id property manually in your model, but you will need to set the value when you sync documents.
  • name: the name of the document model. For example, Post or User.
  • cacheFieldName: (optional) the field to use for caching each document.
  • fields: an object containing each of the document model’s fields. Learn more about field types.
  • localized: (optional) setting this to true flags this model as document-localized. Note that you also have the option to enable field-level localization.

For example, this is how to define a document model called Post that has a title field and a required updatedAt field. The updatedAt field is used for caching. Note that an id isn’t explicitly defined here because Netlify includes one automatically.

connector.model(async ({ define }) => {
define.document({
name: "Post",
cacheFieldName: "updatedAt",
fields: {
title: {
type: "String",
},
updatedAt: {
type: "String",
required: true,
},
},
});
});

The following sections outline some of the properties available to you when you define your model. We’ve also included a detailed example that you can refer to.

Cache field name

To allow Netlify to optimize GraphQL queries for your users, we recommend using cache fields. Netlify uses cache fields to determine what data to re-insert and to allow for GraphQL query caching. Documents are only updated when the value of the cache field changes.

If you don’t set a cache field, Netlify will recreate documents of that type every time models.[ModelName].insert() is called.

When defining a document model, you can specify a top-level model field to use for caching each document.

For example:

connector.model(async ({ define }) => {
define.document({
name: "Post",
cacheFieldName: "updatedAt",
fields: {
title: {
type: "String",
},
updatedAt: {
type: "String",
required: true
},
},
});
});
connector.sync(({ models }) => {
models.Post.insert({
id: "1",
title: "Hello world"
updatedAt: "1689021849725"
})
})

In this example, cacheFieldName is set to the updatedAt field. The Post document will only update if the updatedAt value has changed since the last time models.Post.insert() was called with the same document ID.

Fields

When you define fields for a document model or an object type, you can set the following properties:

  • field name: defined using the object property name for that field. You can use any field name except for internal, id, and fields. For example, this is how we would set the field name updatedAt:
    fields: {
    updatedAt: {
    type: "String",
    required: true,
    },
    }
  • type: defines the type of the field. Learn more about field types.
  • required: (optional) set to true to mark a field as required.
  • list: (optional) set to true to indicate the field is a list. To make the list required, set this property to required instead of true. For example, list: required.
  • localized: (optional) setting this to true flags this field as localized. Note that you also have the option to enable document-level localization.

Field types

Along with the built-in field types, you can use object, enum, and union types that you define yourself. You can also use a document model as a type.

Define an object type

If you have a complex field type on your documents, you can define an object type using define.object(). You define the fields on your object the same way you do on a document model, as documented under fields. You also have the option of specifying editor settings on the object.

Note that object types don’t have insert and delete methods, only document models do.

Once you have defined an object and stored it in a variable, you can use that object type on your document model.

For example:

connector.model(async ({ define }) => {
// this defines an object type that we store in a variable
// called Content
const Content = define.object({
name: "Content",
fields: {
title: {
type: "String",
},
},
});
// this defines a document model `Post` and its `content` field is
// of the `Content` object type defined above
define.document({
name: "Post",
fields: {
title: {
type: "String",
required: true,
},
content: {
type: Content,
},
},
});
});

For unnamed object types, you can also define the models inline using define.inlineObject().

Define an enum type

Enumerated (enum) types are a specific set of strings that you can query or use in filters in a GraphQL API. You can define an enum using define.enum().

For example:

connector.model(async ({ define }) => {
define.enum({
name: "ExampleEnumStoplight",
values: [
{
label: "Green light", // used as the GraphQL description
value: "GREEN", // the actual enum member value
},
{
label: "Yellow light",
value: "YELLOW",
},
{
label: "Red light",
value: "RED",
},
],
});
});

For unnamed enum types, you can also define the models inline using define.inlineEnum().

Define a union type

Union types are combined types that include different object types and/or different document models. You define a union type using define.union().

For example:

const Content = define.union({
name: "ExampleContentUnion",
types: ["Post", "News"]
})
connector.model(async ({ define }) => {
const UserModel = define.document({
name: "User",
fields: {
posts: {
type: Content,
list: true
},
mostPopularPost: {
type: Content
}
}
})
define.document({
name: "News"
fields: {
title: {
type: "String"
}
}
})
define.document({
name: "Post",
fields: {
author: {
type: UserModel
}
}
})
})

If relationship fields are union types, they are required to have the ID and type of the relationship when you insert documents. Learn more about creating documents that have relationship fields.

For unnamed union types, you can also define the models inline using define.inlineUnion().

Inline model definitions

For situations where your type is unnamed in your data source, you may want to define objects, unions, or enums without a name. For example, when a CMS supports non-global type definitions.

To support this, there are inline definition helpers for each of these types:

  • define.inlineObject()
  • define.inlineUnion()
  • define.inlineEnum()

For example:

connector.model(async ({ define }) => {
define.document({
name: "Post",
fields: {
content: {
type: define.inlineUnion({
types: ["Post", "News"],
}),
},
},
});
});

The inline model definitions will still have globally unique names that use the hash of the model definition itself.

So, while non-inline definitions will error if you define them with a duplicate name, you may re-define inline model definitions as many times as you’d like. Since the model name is a hash of the definition, we can share the same model definition in all the places it was re-defined.

Relationship fields

A relationship field is a field that has another document model as its type. It allows you to link from one document to another document by document ID. For example, you may have an authors field on the Post document model that is a list of User documents.

To create a relationship field, set the type of the field to a reference to a document model definition variable, or to the name of a document model.

Learn more about creating documents that have relationship fields.

Cross-reference fields

If your data source supports cross-references, you can define cross-reference types and fields in your connector. Cross-reference fields allow you to specify reference fields across different connectors or across different instances of the same connector. These are cross-references that your data source can resolve.

Cross-references defined by users

Netlify also offers the ability for Connect users to define cross-references across different types of data sources. For example, from Drupal to Contentstack. These cross-references are resolved by Netlify, instead of the individual data source. Connect users can define cross-references in the Netlify UI. Make sure your connector includes an instanceID option to support user-defined cross-references.

The cross-reference type should include the following properties for what this cross-reference is for:

  • modelName: the name of the model
  • connectorName: the name of the connector
  • instanceID: the instance ID of the connector

You can define cross-reference types that reference a single model or multiple models.

For example:

// Reference a single model
connector.model(async ({ define }) => {
const myCrossReference = define.crossReference({
to: {
modelName: `ExampleDocument`,
connectorName: `example-connector`,
instanceID: `1234`,
},
});
// Then define a field as a cross-reference using the type
define.document({
name: "Post",
fields: {
bonusContent: {
type: myCrossReference
},
},
});
});
// Reference multiple models
connector.model(async ({ define }) => {
const myCrossReference = define.crossReference({
to: [
{
modelName: `ExampleDocument`,
connectorName: `example-connector`,
instanceID: `1234`,
},
{
modelName: `AnotherDocument`,
connectorName: `another-connector`,
instanceID: `5678`,
},
],
});
});

You can also define the cross-reference type inline when you define the field. This example defines the bonusContent field as a cross-reference to the AnotherDocument model.

// Define the cross-reference inline
connector.model(({ define })=>{
define.document({
name: `ExampleDocument`,
fields: {
bonusContent: {
type: define.crossReference({
to: {
modelName: `AnotherDocument`,
connectorName: `another-connector`,
instanceID: `5678`,
},
}),
},
},
})
})

Example document model definitions

These detailed examples demonstrate how to define a document model and various types of fields on it. A subset of these options are available to object types also.

Example for Connect

connector.model(async ({ define }) => {
define.document({
name: "Post", // name of the document model
cacheFieldName: "updatedAt", // cache fields only apply to document models
fields: {
updatedAt: { // updatedAt is the field name
type: "String",
required: true, // this is a required field.
},
title: {
type: "String",
},
postContent: {
type: Content, // this object type, `Content`, is defined below
},
author: {
type: "User", // this is a relationship field because `User` is a document model, defined below
list: true,
}
categories: {
type: "String",
list: true, // Post.categories is a list of strings
},
languages: {
type: "String",
list: required,
// In this example all Post documents must include a languages list but the list can be empty.
// for example, models.Post.insert({languages: []})
},
tags: {
type: "String",
required: true,
list: required,
// In this example all Post documents must include a list of tags and the list must include values.
},
},
});
// defines a Content object type
const Content = define.object({
name: "Content",
fields: {
title: {
type: "String",
},
}
});
// defines a User document model
define.document({
name: "User",
fields: {
name: {
type: "String",
},
}
});
});

Got it!

Your feedback helps us improve our docs.