Data Access

Relational Data

Read and write related data across collections.

Read a To-One Relation

Fetch an article with its author by naming the relation under include. Place include alongside fields: fields selects primitive fields, and each relation under include has its own query.

const article = await client.Articles.readOne({
  key: 1,
  fields: ['id', 'title'],
  include: {
    author: { fields: ['name', 'email'] },
  },
});

Read a To-Many Relation

Fetch an author with all their articles. The related items are wrapped in a { data: [...] } envelope.

const author = await client.Authors.readOne({
  key: 1,
  fields: ['id', 'name'],
  include: {
    articles: { fields: ['title', 'status'] },
  },
});

Filter, Sort, and Paginate an Included Relation

Apply filters, sorting, and pagination to related items in a to-many relation. In both REST and the SDK, pass filter, sort, limit, and offset alongside the relation's fields inside include.

const author = await client.Authors.readOne({
  key: 1,
  fields: ['id', 'name'],
  include: {
    articles: {
      fields: ['title', 'status'],
      filter: { status: { _eq: 'published' } },
      sort: [{ created_at: { direction: 'desc' } }],
      limit: 5,
    },
  },
});
Note
Relation parameters inside include have no underscore prefix: filter, sort, limit, offset. Filter operators keep theirs, as in include[articles][filter][status][_eq]=published.

For more on field selection patterns, see Field Selection.


Nested Relational Operations

When creating or updating items, you can modify related items in the same request using nested relational operations. This avoids multiple round-trips and ensures all changes happen atomically within a single transaction.

There are five operations available:

OperationDescriptionAvailable In
_connectLink an existing item by its primary keyCreate, Update
_createCreate a new related item inlineCreate, Update
_disconnectUnlink a related item without deleting itUpdate
_updateModify a related item's fields in placeUpdate
_deletePermanently remove a related itemUpdate

In create context, only _connect and _create are available — you cannot disconnect, update, or delete relations that don't exist yet. In update context, all five operations are available and can be combined in a single request.

Operations are expressed as entries in an array on the relation field. They execute in the order they appear in the array, giving you precise control over the sequence of changes.


Use _connect to link an existing item by its primary key. Available in both create and update contexts.

To-One

To-one _connect uses key (singular object). In create context, the relation field is a singular object. In update context, it is wrapped in an array.

// Create context — singular object
const article = await client.Articles.createOne({
  data: {
    title: 'New Article',
    author: {
      _connect: { key: { id: 7 } },
    },
  },
  fields: ['id', 'title'],
  include: {
    author: { fields: ['id', 'name'] },
  },
});

// Update context — array-wrapped
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    author: [{ _connect: { key: { id: 7 } } }],
  },
  fields: ['id', 'title'],
  include: {
    author: { fields: ['id', 'name'] },
  },
});

To-Many

To-many _connect uses keys (plural, array of key objects).

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    tags: [
      { _connect: { keys: [{ id: 1 }, { id: 3 }, { id: 5 }] } },
    ],
  },
  fields: ['id'],
  include: {
    tags: { fields: ['id', 'name'] },
  },
});
Note
To-one _connect uses key (singular object). To-many _connect uses keys (plural, array of objects).
Warning
If the key does not exist, the operation fails and the entire transaction rolls back. Relations that span two data sources are the exception — see Relations Across Data Sources.

Use _create to create a new related item and link it in one operation. Available in both create and update contexts.

To-One

To-one _create takes a singular data object.

const article = await client.Articles.createOne({
  data: {
    title: 'Getting Started',
    author: {
      _create: { data: { name: 'Jane Doe', email: 'jane@example.com' } },
    },
  },
  fields: ['id', 'title'],
  include: {
    author: { fields: ['id', 'name'] },
  },
});

To-Many

To-many _create takes an array of objects in data.

// Create context — create parent with new related items
const author = await client.Authors.createOne({
  data: {
    name: 'New Author',
    articles: [
      {
        _create: {
          data: [
            { title: 'First Article' },
            { title: 'Second Article' },
          ],
        },
      },
    ],
  },
  fields: ['id', 'name'],
  include: {
    articles: { fields: ['title'] },
  },
});

// Update context — add new related items to existing parent
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    comments: [
      { _create: { data: [{ body: 'Great post!' }, { body: 'Thanks for sharing!' }] } },
    ],
  },
  fields: ['id'],
  include: {
    comments: { fields: ['id', 'body'] },
  },
});
Note
To-one _create takes a singular data object. To-many _create takes an array of objects.

Use _disconnect to remove a link without deleting the related item. Only available in update context.

To-One

To-one _disconnect takes an empty object {} — there is only one link to remove.

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    author: [{ _disconnect: {} }],
  },
  fields: ['id', 'title'],
});
Tip
Use _disconnect instead of _delete when you want to keep the related item. For example, unassigning an author from an article without deleting the author.

To-Many

To-many _disconnect takes a filter object. Omit filter to disconnect all related items. An empty filter: {} is rejected with a 422.

// Disconnect items matching a filter
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    comments: [
      { _disconnect: { filter: { status: { _eq: 'draft' } } } },
    ],
  },
  fields: ['id'],
  include: {
    comments: { fields: ['id', 'body', 'status'] },
  },
});

// Disconnect all
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    tags: [{ _disconnect: {} }],
  },
  fields: ['id'],
});

Use _update to modify a related item's fields without changing the link itself. Only available in update context. Takes data (the fields to change) and an optional filter to target specific items.

To-One

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    author: [{ _update: { data: { name: 'Jane Smith' } } }],
  },
  fields: ['id'],
  include: {
    author: { fields: ['id', 'name'] },
  },
});

To-Many

Pass both filter and data. Omit filter to update all related items.

// Update matching items by filter
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    comments: [
      { _update: { filter: { status: { _eq: 'pending' } }, data: { status: 'approved' } } },
    ],
  },
  fields: ['id'],
  include: {
    comments: { fields: ['id', 'body', 'status'] },
  },
});

// Update all (omit filter)
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    comments: [
      { _update: { data: { reviewed: true } } },
    ],
  },
  fields: ['id'],
});

Use _delete to permanently remove related items from the database. Only available in update context. Takes an optional filter to target specific items.

To-One

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    author: [{ _delete: {} }],
  },
  fields: ['id', 'title'],
});

To-Many

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    comments: [
      { _delete: { filter: { flagged: { _eq: true } } } },
    ],
  },
  fields: ['id'],
  include: {
    comments: { fields: ['id', 'body'] },
  },
});
Warning
_delete permanently removes items from the database. Use _disconnect to remove only the link.

Combine Multiple Operations

Multiple relational operations on the same field are expressed as separate entries in the array. Operations process in the order they appear.

const article = await client.Articles.updateOne({
  key: 1,
  data: {
    tags: [
      // 1. Remove links to specific tags
      { _disconnect: { filter: { name: { _starts_with: 'draft' } } } },
      // 2. Delete tags that are obsolete
      { _delete: { filter: { id: { _in: [10, 11] } } } },
      // 3. Update remaining tags
      { _update: { data: { reviewed: true } } },
      // 4. Create a new tag
      { _create: { data: [{ name: 'graphql' }] } },
      // 5. Connect existing tags
      { _connect: { keys: [{ id: 7 }] } },
    ],
  },
  fields: ['id'],
  include: {
    tags: { fields: ['id', 'name'] },
  },
});
Note
Operations are processed in the order they appear in the array. Place disconnects and deletes before connects and creates to avoid conflicts like duplicate links or constraint violations.

Nest Across Multiple Levels

Relational operations compose at any depth. Create an article with a new author and comments, where comments themselves link to existing authors.

const article = await client.Articles.createOne({
  data: {
    title: 'New Article',
    author: { _create: { data: { name: 'Jane' } } },
    comments: [
      {
        _create: {
          data: [
            { body: 'First!', author: { _connect: { key: { id: 1 } } } },
          ],
        },
      },
    ],
  },
  fields: ['id', 'title'],
  include: {
    author: { fields: ['id', 'name'] },
    comments: {
      fields: ['id', 'body'],
      include: {
        author: { fields: ['id', 'name'] },
      },
    },
  },
});

Context Differences (Create vs Update)

The available operations and syntax differ between create and update contexts.

Create Context

Only _connect and _create are available. You cannot disconnect, update, or delete relations that do not exist yet.

  • To-one: the relation field is a singular object containing one operation
  • To-many: the relation field is an array where each element is one operation
Create context
const article = await client.Articles.createOne({
  data: {
    title: 'New Article',
    // To-one: singular object
    author: { _connect: { key: { id: 1 } } },
    // To-many: array of operations
    comments: [
      { _create: { data: [{ body: 'Welcome!' }] } },
      { _connect: { keys: [{ id: 100 }, { id: 101 }] } },
    ],
  },
  fields: ['id'],
});

Update Context

All five operations are available: _connect, _create, _disconnect, _update, _delete.

Both to-one and to-many relations use arrays in update context. This enables combining multiple operations on the same field — for example, disconnecting the current author then connecting a new one:

Update context
const article = await client.Articles.updateOne({
  key: 1,
  data: {
    // To-one: array wrapping in update context
    author: [
      { _disconnect: {} },
      { _connect: { key: { id: 5 } } },
    ],
    // To-many: also array wrapping
    comments: [
      { _disconnect: { filter: { status: { _eq: 'spam' } } } },
      { _delete: { filter: { flagged: { _eq: true } } } },
      { _update: { data: { status: 'reviewed' }, filter: { status: { _eq: 'pending' } } } },
      { _create: { data: [{ body: 'Pinned comment' }] } },
    ],
  },
  fields: ['id'],
});
Warning
In update context, both to-one and to-many relations are wrapped in arrays. This differs from create context where to-one is a singular object.

Quick Reference

OperationTo-One (Create)To-One (Update)To-Many (Create)To-Many (Update)
Create{ _create: { data: { ... } } }[{ _create: { data: { ... } } }][{ _create: { data: [{ ... }] } }][{ _create: { data: [{ ... }] } }]
Connect{ _connect: { key: { id } } }[{ _connect: { key: { id } } }][{ _connect: { keys: [{ id }] } }][{ _connect: { keys: [{ id }] } }]
DisconnectN/A[{ _disconnect: {} }]N/A[{ _disconnect: { filter: { ... } } }]
UpdateN/A[{ _update: { data: { ... } } }]N/A[{ _update: { filter: { ... }, data: { ... } } }]
DeleteN/A[{ _delete: {} }]N/A[{ _delete: { filter: { ... } } }]

Key differences:

  • Create context supports only _connect and _create. Update context supports all five operations.
  • To-one in create context is a singular object. All update context relations use arrays.
  • Connect uses key (singular) for to-one, keys (plural array) for to-many.
  • Disconnect takes an empty object {} for to-one (there is only one link), a filter for to-many.
  • Create takes a single data object for to-one, an array for to-many.

Relations Across Data Sources

A relation can span two data sources. Monospace resolves it at query time, and no foreign key constraint exists across the boundary, so nothing enforces that a reference resolves.

  • A reference to a missing item resolves to null on a to-one relation, and the item is omitted from a to-many relation. Neither errors.
  • _connect succeeds against a key that does not exist when the relation stores that key on the collection being written. Monospace writes the key without reading the other data source.

Treat a cross-data-source relation as nullable no matter how its backing field is declared. See Cross-Data-Source Referential Integrity.


Atomicity

All relational operations within a single request execute atomically within a single data source. If any operation fails, the entire request rolls back — no partial writes are persisted.

Warning
Atomicity is guaranteed only within a single data source. When a request crosses data source boundaries (e.g., writing to collections backed by different databases), there are no transactional guarantees across those boundaries.

See Also

  • Field Selection — wildcards, related data, and advanced patterns
  • Filtering — filter syntax used in _disconnect, _update, and _delete
  • Writing Data — basic create, update, and delete operations
  • Type System — input types for create and update payloads

MonospaceThe governed API layer for every app, person, and agent.

Copyright © 2026 Monospace Inc.