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 tofalse, 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.
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.| Feature | SDK <4.10: DQL_STRICT_MODE=true | SDK 4.11+: DQL_STRICT_MODE=false |
|---|---|---|
| Nested MAPs | Requires explicit collection definitions | Automatically inferred |
| Collection Definitions | Required to use non-register CRDT types | Only required to use register objects |
| Legacy Compatibility | Difficult, due to collection definitions | Supported out of the box |
| Default object type | REGISTER (whole object replacement) | MAP (field-level merging) |
| Nested field updates | Replaces entire object | Merges individual fields |
HTTP Usage
To use the HTTP API with strict mode disabled, use the/api/v5/store/execute endpoint.
Endpoint Compatibility
| Endpoint | Supports DQL_STRICT_MODE=false | Use When |
|---|---|---|
/api/v4/store/execute | ❌ No | All SDK peers have DQL_STRICT_MODE=true |
/api/v5/store/execute | ✅ Yes | Any peers have DQL_STRICT_MODE=false |
SDK Usage
First, disable strict mode before callingstartSync or creating your DQL subscriptions, observers, or execute statements.
Subscriptions
Subscriptions behave the same regardless of whetherDQL_STRICT_MODE is enabled or disabled.
Examples
WithDQL_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.
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, useUPDATE statement to set nested MAPs.
Using UPDATE
Updating Nested Maps with Dynamic Keys
UseINSERT 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 withDQL_STRICT_MODE=false, use the UNSET statement.
Register Objects
AREGISTER 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
Last Write Wins
WithDQL_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.
Cross-Peer Synchronization
Version Compatibility
| Scenario | v4.10 and earlier | v4.11+ |
|---|---|---|
| All peers same setting | Requires collection definitions | Works with or without definitions |
| Mixed settings, no definitions | Nested updates may fail | Objects default to REGISTER on strict=true peers |
| Mixed settings, with explicit MAP definitions | Works correctly | Works correctly |
Mixing Peers with Different Settings
You can mix peers with different strict mode settings, but understanding the behavior is crucial for proper data synchronization:Matched Settings (Recommended)
When all peers use the sameDQL_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 differentDQL_STRICT_MODE settings:
- Data DOES sync between peers regardless of settings differences
- Default type behavior changes:
- Peer with
DQL_STRICT_MODE=truetreats undefined objects as REGISTERs - Peer with
DQL_STRICT_MODE=falsetreats objects as MAPs
- Peer with
- This affects nested field updates significantly
- Option 1: Match strict mode settings across all peers (simplest)
- 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
- Check strict mode on all peers:
- Verify your update query:
- Check how the data appears on each peer:
Real-World Example Fix
Problem: Updating nested fields:DQL_STRICT_MODE=true while the updating peer has DQL_STRICT_MODE=false. Without a MAP definition, metadata is treated as a REGISTER.
Solutions: