Operation Reference
Operation Format
Every operation is an object with a kind and a data payload. Operations are grouped below by the entity they act on: namespaces, collections, fields, indexes, and relations.
New entities carry a client-generated UUID id that you reuse to reference them in later operations. Field and index operations nest inside createCollection and updateCollection; every other operation is top-level. Within a collection, Monospace sorts the nested field and index operations into a safe order. Top-level operations run in the order you send them, so order them yourself when one depends on another — create a collection before the relation that references it.
Collections and fields carry two names. dbName is the object's storage name in the data source. apiName is the name used in the data API: a collection is served at /items/{apiName}, and fields are addressed by apiName in query parameters. When apiName is omitted, it falls back to dbName.
Namespaces
A namespace is a schema in the data source that groups related collections.
Create a Namespace
createNamespace adds a namespace to a data source.
{
"kind": "createNamespace",
"data": {
"id": "3c1d2e3f-0001-4a1a-9c1a-000000000001",
"sourceId": "d7c8f0e2-9b3a-4c1e-8f2a-1a2b3c4d5e6f",
"dbName": "reporting"
}
}
Rename a Namespace
renameNamespace changes a namespace's dbName.
{
"kind": "renameNamespace",
"data": {
"id": "3c1d2e3f-0001-4a1a-9c1a-000000000001",
"dbName": "analytics"
}
}
Delete a Namespace
deleteNamespace removes a namespace.
{
"kind": "deleteNamespace",
"data": { "id": "3c1d2e3f-0001-4a1a-9c1a-000000000001" }
}
Collections
A collection is a container for items of the same type. createCollection and updateCollection carry nested field and index operations in their own data.operations array.
Create a Collection
createCollection adds a collection and, optionally, its initial fields and indexes.
{
"kind": "createCollection",
"data": {
"id": "f4ccfb8e-5d79-4d2f-83b0-5b749f784b00",
"sourceId": "d7c8f0e2-9b3a-4c1e-8f2a-1a2b3c4d5e6f",
"namespaceId": null,
"dbName": "articles",
"apiName": "articles",
"operations": []
}
}
namespaceId—nullplaces the collection in the data source's default namespace. On the built-in system source, it places the collection in the project's_datanamespace.apiName—nullfalls back todbNameoperations— nestedcreatePrimitiveFieldandcreateIndexoperations
Update a Collection
updateCollection applies nested field and index operations to an existing collection.
{
"kind": "updateCollection",
"data": {
"id": "f4ccfb8e-5d79-4d2f-83b0-5b749f784b00",
"operations": []
}
}
The nested operations array accepts createPrimitiveField, updatePrimitiveField, deletePrimitiveField, createIndex, and deleteIndex.
Rename a Collection
renameCollection changes a collection's dbName, apiName, or both. Set either to null to leave it unchanged.
{
"kind": "renameCollection",
"data": {
"id": "f4ccfb8e-5d79-4d2f-83b0-5b749f784b00",
"dbName": "posts",
"apiName": "posts"
}
}
Delete a Collection
deleteCollection removes a collection.
{
"kind": "deleteCollection",
"data": { "id": "f4ccfb8e-5d79-4d2f-83b0-5b749f784b00" }
}
Fields
A field is a named value on an item. createPrimitiveField, updatePrimitiveField, and deletePrimitiveField nest inside createCollection or updateCollection. renamePrimitiveField is top-level.
Create a Field
createPrimitiveField adds a field to a collection.
{
"kind": "createPrimitiveField",
"data": {
"id": "9568d51f-2dde-40c1-93e4-98cc3feaca53",
"dbName": "title",
"apiName": "title",
"type": { "name": "string", "params": { "length": "unlimited" } },
"isNullable": false,
"isList": false,
"defaultValue": null
}
}
type— the field type object (see Field Types)isList—truestores an array of the typedefaultValue— a default value object, ornullfor no default
Update a Field
updatePrimitiveField applies one or more change operations to a field.
{
"kind": "updatePrimitiveField",
"data": {
"id": "9568d51f-2dde-40c1-93e4-98cc3feaca53",
"operations": [
{ "kind": "changeIsNullable", "data": { "isNullable": true } }
]
}
}
The nested operations array accepts:
| Kind | data | Changes |
|---|---|---|
changeType | { "type": { ... } } | The field's type |
changeIsNullable | { "isNullable": true } | Whether the field accepts null |
changeIsList | { "isList": true } | Whether the field stores an array |
changeDefaultValue | { "defaultValue": ... } | The field's default value |
Delete a Field
deletePrimitiveField removes a field from a collection.
{
"kind": "deletePrimitiveField",
"data": { "id": "9568d51f-2dde-40c1-93e4-98cc3feaca53" }
}
Rename a Field
renamePrimitiveField changes a field's dbName, apiName, or both. This is a top-level operation. Set either to null to leave it unchanged.
{
"kind": "renamePrimitiveField",
"data": {
"id": "9568d51f-2dde-40c1-93e4-98cc3feaca53",
"dbName": "headline",
"apiName": "headline"
}
}
Indexes
createIndex and deleteIndex nest inside createCollection or updateCollection.
Create an Index
createIndex adds an index over one or more fields.
{
"kind": "createIndex",
"data": {
"id": "d78c36f9-b627-433c-88bb-58eaadba50e1",
"dbName": "articles_slug_idx",
"fieldIds": ["9568d51f-2dde-40c1-93e4-98cc3feaca53"],
"kind": "unique"
}
}
fieldIds— the indexed fields, in order; at least one is requiredkind—unique,normal, orprimary(primarybacks a collection's primary key and makes every indexed field non-nullable)
Delete an Index
deleteIndex removes an index.
{
"kind": "deleteIndex",
"data": { "id": "d78c36f9-b627-433c-88bb-58eaadba50e1" }
}
Relations
A relation links two collections. createRelationFieldPair creates both sides at once — one relation field on each collection. The unconstrained side's isList sets whether the relation is to-many or to-one.
Create a Relation
createRelationFieldPair creates a bidirectional relation. Its data is itself tagged with kind: "single".
{
"kind": "createRelationFieldPair",
"data": {
"kind": "single",
"data": {
"relationName": "article_author",
"onUpdate": "noAction",
"onDelete": "setNull",
"firstField": {
"id": "11111111-1111-4a1a-9c1a-aaaaaaaaaaaa",
"apiName": "author",
"isList": false,
"isConstrained": true,
"collectionId": "f4ccfb8e-5d79-4d2f-83b0-5b749f784b00",
"fieldIds": ["d4e5f6a7-4444-4a1a-9c1a-555566667777"]
},
"secondField": {
"id": "22222222-2222-4a1a-9c1a-bbbbbbbbbbbb",
"apiName": "articles",
"isList": true,
"isConstrained": false,
"collectionId": "a83bda5c-96bd-462f-8012-481115dfa751",
"fieldIds": ["b8c9d0e1-6666-4a1a-9c1a-777788889999"]
}
}
}
}
Each side is a relation field:
isConstrained—trueon the side that holds the foreign key; set it tofalseon the other sideisList— setfalseon the constrained side; set it totrueon the unconstrained side for a to-many relation, orfalsefor a to-one relationcollectionId— the collection the field belongs tofieldIds— the backing fields: the foreign key on the constrained side, the referenced key on the other
The fields in fieldIds are primitive fields that must already exist in each collection — create them before the relation references them. When a side lists more than one field, the two sides map positionally: the first field on the constrained side pairs with the first on the referenced side, and so on.
onUpdate and onDelete actions.onUpdate and onDelete default to noAction when omitted. Both accept one of:
| Action | Behavior |
|---|---|
noAction | Makes no automatic change. The data source performs its normal referential-integrity check. |
cascade | Cascades the delete or update through the relation. |
restrict | Blocks the operation while related items exist. |
setNull | Sets the dependent backing fields to null. It fails when any backing field is non-nullable. |
setDefault | Sets the dependent backing fields to their defaults. It fails when a backing field has no default. |
Update Relation Actions
updateRelationFieldPair changes one or both referential actions on an existing relation. Include both relation field IDs and at least one action.
{
"kind": "updateRelationFieldPair",
"data": {
"firstFieldId": "11111111-1111-4a1a-9c1a-aaaaaaaaaaaa",
"secondFieldId": "22222222-2222-4a1a-9c1a-bbbbbbbbbbbb",
"onDelete": "cascade"
}
}
Omit an action to leave it unchanged. Set an action to null to reset it to noAction.
Delete a Relation
deleteRelationFieldPair removes both sides of a relation, identified by their field IDs.
{
"kind": "deleteRelationFieldPair",
"data": {
"firstFieldId": "11111111-1111-4a1a-9c1a-aaaaaaaaaaaa",
"secondFieldId": "22222222-2222-4a1a-9c1a-bbbbbbbbbbbb"
}
}
Rename a Relation Field
renameRelationField changes the apiName of one side of a relation.
{
"kind": "renameRelationField",
"data": {
"id": "11111111-1111-4a1a-9c1a-aaaaaaaaaaaa",
"apiName": "writer"
}
}
Field Types
The type object tags a primitive type by name. Most types need only a name. string and decimal carry a params object.
{ "name": "boolean" }
{ "name": "string", "params": { "length": "unlimited" } }
{ "name": "string", "params": { "length": "fixed", "value": 255 } }
{ "name": "decimal", "params": { "precision": 10, "scale": 2 } }
| Category | Names |
|---|---|
| Integer | int8, int16, int32, int64, uint8, uint16, uint32, uint64 |
| Floating point | float32, float64, decimal |
| Text | string, uuid |
| Boolean | boolean |
| Temporal | date, time, dateTime, dateTimeWithTimezone |
| Other | json, bytes, geometry |
uint8–uint64) and geometry are part of the format but cannot currently be applied to a data source — a migration that uses them fails. Introspection may also report a field as unsupported when Monospace does not model its type; you cannot author that type.Default Values
A field's defaultValue is null for no default, or an object tagged by source. Two sources exist.
Engine-Managed Defaults
An engine-managed default resolves a value whenever the field is omitted from a write. It fills the gap — it never overrides a value the caller provides.
Defaults resolve per write phase. onCreate and onUpdate are independent and each optional.
| Field | Applies when |
|---|---|
onCreate | The field is omitted while creating an item |
onUpdate | The field is omitted while updating an item |
Set only the phase you need. onCreate alone applies a value on create and leaves updates untouched; onUpdate alone applies a value on update but not on create. An engineManaged default must set at least one of the two — for no default at all, use defaultValue: null.
Each phase holds a source object tagged by kind. Both phases resolve against the field's single type, so keep them consistent — a static boolean on create paired with a currentTimestamp on update describes two different types for one field.
kind | Fields | Resolves to |
|---|---|---|
static | valueEncoded | A literal value, encoded as JSON |
currentTimestamp | — | The write time (now()) |
currentUser | — | The authenticated user's key |
randomUuid | version (v4 or v7) | A newly generated UUID |
static value must be compatible with the field's type. currentTimestamp requires a date, time, dateTime, or dateTimeWithTimezone field. currentUser and randomUuid require a uuid field.valueEncoded does not preserve the value's type. Monospace coerces it using the field's type while applying the write, so 2 supplied for a string field becomes "2".
Set onCreate alone for a value fixed at insert time.
A status field that starts at "draft":
{ "source": "engineManaged", "onCreate": { "kind": "static", "valueEncoded": "draft" } }
A created_at field stamped once, when the item is created:
{ "source": "engineManaged", "onCreate": { "kind": "currentTimestamp" } }
A created_by field set to whoever creates the item:
{ "source": "engineManaged", "onCreate": { "kind": "currentUser" } }
An id field that receives a UUID v7 when the item is created:
{ "source": "engineManaged", "onCreate": { "kind": "randomUuid", "version": "v7" } }
Set both phases to keep a field current on every write — this is what the phase split buys you.
An updated_at field stamped on create and refreshed on each later update:
{
"source": "engineManaged",
"onCreate": { "kind": "currentTimestamp" },
"onUpdate": { "kind": "currentTimestamp" }
}
An updated_by field set to the creator, then to whoever updates the item next:
{
"source": "engineManaged",
"onCreate": { "kind": "currentUser" },
"onUpdate": { "kind": "currentUser" }
}
Connector-Managed Defaults
The data source owns the value, as with autoincrement, serial, or identity fields. Introspection reports these; you rarely author them.
{ "source": "connectorManaged", "isAutoincrement": true }
isAutoincrement is true for autoincrement fields, or null when the connector does not report it.
See Also
- Apply Migrations — send these operations to a project
- Import Changes — read operations returned by a diff
- Data Types — how these types map to database types
- Schema Migration Overview — authentication, format, and error codes