Skip to main content

Introduction

With strict mode enabled (the default for all Ditto 4.x SDKs), all fields are treated as a register by default. When enabled, every field in a document must match the collection definition exactly, including its CRDT type (e.g., map, register, counter). Disabling strict mode enables new functionality: when set to false, collection definitions are no longer required when using multiple CRDT types.
SELECT queries will return and display all fields when strict mode is disabled. This matches the behavior of the legacy query language, objects in INSERT and UPDATE statements are treated as maps. When a field has multiple possible CRDT types, the most recently updated type is chosen.
When strict mode is disabled, Ditto will infer the CRDT type based on the document’s shape:
  • Objects → treated as CRDT maps
  • Scalars and arrays → treated as Registers
  • Counters and attachments → inferred from operations
Important for Cross-Peer SynchronizationWhen peers have different DQL_STRICT_MODE settings:
  • Data WILL sync between peers, but behavior changes
  • When strict mode is true (SDK 4.10 and earlier), nested objects default to REGISTER type
  • For consistent behavior, either use matching strict mode settings across all peers OR explicitly define MAP types in your collection definitions in 4.10 and below.
See Cross-Peer Synchronization for detailed examples.

Strict Mode Behavior

Strict mode is enabled (set to true) by default in SDK 4.x, but will be set to false by default in SDK 5.0 and later. If you are using SDK 4.11+, ensure you have configured strict mode by setting DQL_STRICT_MODE=false, before starting your sync.

HTTP Usage

To use the HTTP API with strict mode disabled, use the /api/v5/store/execute endpoint.

Endpoint Compatibility

The v5 and v4 HTTP APIs are compatible, so you can use the v5 endpoint and changes will sync to v4 clients without issues. Example HTTP Request:
Read more about the HTTP API.

SDK Usage

Strict mode defaults differ by SDK version:
  • SDK 4.x: DQL_STRICT_MODE defaults to true
  • SDK 5.0+: DQL_STRICT_MODE defaults to false
If you need to change from the default, set DQL_STRICT_MODE before starting sync or running any DQL queries or registering observers. Subscriptions can be registered before changing the setting.

Subscriptions

Subscriptions behave the same regardless of whether DQL_STRICT_MODE is enabled or disabled.

Examples

With DQL_STRICT_MODE=false, objects are treated as maps. In the following examples, items is an object so it is treated as a map, with further nested objects, which are also treated as maps. This nested structure is common in document databases.
We can insert the document into the database using DQL.
With DQL_STRICT_MODE=false, Ditto infers the CRDT type based on the document’s shape.
No collection definition is required.
  • When you define an object like items: {"shake": ..., "fries": ...} , Ditto treats that as a MAP when strict mode is disabled.
  • Whether you use Query Builder or DQL, this structure is preserved and behaves the same on both insert and read.
  • This is useful to know if you’re working with dynamic key-value structures inside documents.

Updating Nested Maps

In 4.11 and above, use UPDATE statement to set nested MAPs. Using UPDATE

Updating Nested Maps with Dynamic Keys

Use INSERT and ON ID CONFLICT DO UPDATE to update nested maps with dynamic UUIDs. This is the same behavior as using upsert in the legacy query builder.

Deleting Nested Values

In 4.11 and above with DQL_STRICT_MODE=false, use the UNSET statement.

Register Objects

A REGISTER is a data type in Ditto that stores a single scalar value and uses last-write-wins merge strategy to atomically handle conflicts, rather than maps which use add-wins to create a merged object. With DQL_STRICT_MODE=false, if you want a register object (JSON-like object) data type in DQL, it must be specified explicitly. Key characteristics of registers:
  • Stores primitive types (string, boolean) or JSON-like objects
  • Last-write-wins conflict resolution ensures consistent values across peers
For more information on using types and definitions, see DQL > Types and Definitions.

Last Write Wins

With DQL_STRICT_MODE=false, if there is no collection definition provided, writing objects will update a map, even if a register already exists for that field in Ditto. This is called “last write wins” behavior, and it means that the last operation to write to a field will overwrite any previous values, regardless of the type of the field.
If you do not supply the collection definition, a register can be treated as a nested map which can lead to unexpected behavior. It is recommended that you always supply a collection definition if you want to force Ditto to use a register.

Cross-Peer Synchronization

Version Compatibility

Mixing Peers with Different Settings

You can mix peers with different strict mode settings, but understanding the behavior is crucial for proper data synchronization: When all peers use the same DQL_STRICT_MODE setting, behavior is predictable:
  • With false: Objects are treated as MAPs by default, nested updates work as expected
  • With true: Collection definitions are required, types must be explicitly defined

Mismatched Settings (Requires Careful Handling, Avoid Unless Necessary)

When peers have different DQL_STRICT_MODE settings:
  1. Data DOES sync between peers regardless of settings differences
  2. Default type behavior changes:
    • Peer with DQL_STRICT_MODE=true treats undefined objects as REGISTERs
    • Peer with DQL_STRICT_MODE=false treats objects as MAPs
  3. This affects nested field updates significantly
Example of the Issue:
Solution Options:
  1. Option 1: Match strict mode settings across all peers (simplest)
  1. Option 2: Explicitly define MAP types in collection definitions

Troubleshooting Nested Field Sync Issues

Common Symptom: “Nested fields are not syncing”

This is often caused by mismatched strict mode settings between peers without explicit MAP definitions. In this case, the fields have actually all been synchronized correctly, but when querying the data is not displayed as expected.

Diagnostic Steps

  1. Check strict mode on all peers:
  1. Verify your update query:
  1. Check how the data appears on each peer:

Real-World Example Fix

Problem: Updating nested fields:
Root Cause: The client has DQL_STRICT_MODE=true while the updating peer has DQL_STRICT_MODE=false. Without a MAP definition, metadata is treated as a REGISTER. Solutions: