Strict Mode
Available in v4.11 and later, strict mode helps Ditto enforce structure and type safety in your collections.
Introduction
With strict mode enabled, 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. SELECT queries
will return and display all fields by default. 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 types, the most recently updated type
is chosen.
Compatibility
Feature | DQL_STRICT_MODE=true | DQL_STRICT_MODE=false |
---|---|---|
Nested MAPs | ❌ Difficult to use | ✅ Supported |
Collection Definitions | ❌ Required | ✅ Optional |
Legacy Compatibility | ❌ Not supported | ✅ Supported |
Strict mode is enabled (set to true
) in v4, but will be set to false
in v5.
HTTP Usage
To use the HTTP API with strict mode, you can use /store/v5/execute
endpoint.
This endpoint allows you to execute DQL statements without needing to define
collection types.
v5 and v4 API are compatible, so you can use
/store/v5/execute
and changes will sync to v4 clients without issues.
SDK Usage
First, disable strict mode before calling startSync
or creating your DQL subscriptions, observers, or execute
statements.
Subscriptions
Subscriptions behave the same regardless of whether DQL_STRICT_MODE
is enabled or disabled.
Example
With DQL_STRICT_MODE=false
, objects are treated as maps. In the following
examples, items
is a map: each key points to some object. This structure is
common in NoSQL/document-style databases.
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 MERGE
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 Maps
A REGISTER
is a data type in Ditto that stores a single scalar value and
uses last-write-wins merge strategy for handling conflicts.
With DQL_STRICT_MODE=false
, if you want a REGISTER JSON object data type in DQL, it must be
specified explicitly.
Key characteristics of REGISTER:
- Stores primitive types (string, boolean) or JSON objects
- Last-write-wins conflict resolution ensures consistent values across peers
For more information on using types and definitions, see DQL > Types and Definitions.
How it works
When DQL_STRICT_MODE=false
, Ditto is more flexible and will infer the
CRDT type based on the document’s shape, meaning that collection definitions
are no longer required:
- Objects → treated as CRDT maps
- Scalars and arrays → treated as Registers
- Counters and attachments → inferred from operations
When strict mode is set to true
, Ditto infers the CRDT type based on the
document’s shape. By default, objects treated as registers, which means that
every field’s type must be specified in the collection definition.
In this case, the items field is a map, so it must be defined as such in the
collection definition.
Last Write Wins
With DQL_STRICT_MODE=false
, if there is no collection definition provided,
objects will be implicitly updating the MAP after a dot update operation, even
if a register exists on disk.
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 data loss. This means it is recommended that you always supply a collection definition if you want to force Ditto to use a register.
❌ Do not do this
✅ Do this instead:
Mixing Peers
When strict mode is enabled, you can mix peers with different strict mode settings. This is because subscriptions are the same regardless of whether strict mode is enabled or disabled.