ObjectStackObjectStack

Data Engine

Data Engine protocol schemas

Data Engine Protocol

Defines the standard interface for data persistence engines in ObjectStack. This protocol abstracts the underlying storage mechanism (SQL, NoSQL, API, Memory), allowing the ObjectQL engine to execute standardized CRUD and Aggregation operations regardless of where the data resides.

The Data Engine acts as the "Driver" layer in the Hexagonal Architecture.

Source: packages/spec/src/data/data-engine.zod.ts

TypeScript Usage

import { BaseEngineOptionsSchema, DataEngineAggregateOptionsSchema, DataEngineAggregateRequestSchema, DataEngineCountOptionsSchema, DataEngineCountRequestSchema, DataEngineDeleteOptionsSchema, DataEngineDeleteRequestSchema, DataEngineExecuteRequestSchema, DataEngineFilterSchema, DataEngineFindOneRequestSchema, DataEngineFindRequestSchema, DataEngineInsertOptionsSchema, DataEngineInsertRequestSchema, DataEngineQueryOptionsSchema, DataEngineRequestSchema, DataEngineSortSchema, DataEngineUpdateOptionsSchema, DataEngineUpdateRequestSchema, DataEngineVectorFindRequestSchema, DroppedFieldsEventSchema, EngineAggregateOptionsSchema, EngineCountOptionsSchema, EngineDeleteOptionsSchema, EngineQueryOptionsSchema, EngineUpdateOptionsSchema } from '@objectstack/spec/data';
import type { BaseEngineOptions, DataEngineAggregateOptions, DataEngineCountOptions, DataEngineDeleteOptions, DataEngineExecuteRequest, DataEngineFilter, DataEngineInsertOptions, DataEngineInsertRequest, DataEngineQueryOptions, DataEngineRequest, DataEngineSort, DataEngineUpdateOptions, DataEngineVectorFindRequest, DroppedFieldsEvent, EngineAggregateOptions, EngineCountOptions, EngineDeleteOptions, EngineQueryOptions, EngineUpdateOptions } from '@objectstack/spec/data';

// Validate data
const result = BaseEngineOptionsSchema.parse(data);

BaseEngineOptions

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional

DataEngineAggregateOptions

Options for DataEngine.aggregate operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
filterRecord<string, any> | anyoptionalData Engine query filter conditions
groupBystring[]optional
aggregations{ field: string; method: Enum<'count' | 'sum' | 'avg' | 'min' | 'max' | 'count_distinct'>; alias?: string }[]optional

DataEngineAggregateRequest

Properties

PropertyTypeRequiredDescription
method'aggregate'
objectstring
query{ context?: object; where?: Record<string, any> | any; groupBy?: (string | object)[]; aggregations?: object[]; … }

DataEngineCountOptions

Options for DataEngine.count operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
filterRecord<string, any> | anyoptionalData Engine query filter conditions

DataEngineCountRequest

Properties

PropertyTypeRequiredDescription
method'count'
objectstring
query{ context?: object; where?: Record<string, any> | any; filter?: Record<string, any> | any }optional

DataEngineDeleteOptions

Options for DataEngine.delete operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
filterRecord<string, any> | anyoptionalData Engine query filter conditions
multibooleanoptional

DataEngineDeleteRequest

Properties

PropertyTypeRequiredDescription
method'delete'
objectstring
idstring | numberoptionalID for single delete, or use where in options
options{ context?: object; where?: Record<string, any> | any; multi?: boolean; filter?: Record<string, any> | any }optional

DataEngineExecuteRequest

Properties

PropertyTypeRequiredDescription
method'execute'
commandany
optionsRecord<string, any>optional

DataEngineFilter

Data Engine query filter conditions

Union Options

This schema accepts one of the following structures:

Option 1

Type: Record<string, any>


Option 2

Reference: any



DataEngineFindOneRequest

Properties

PropertyTypeRequiredDescription
method'findOne'
objectstring
query{ context?: object; where?: Record<string, any> | any; fields?: string[]; orderBy?: object[]; … }optional

DataEngineFindRequest

Properties

PropertyTypeRequiredDescription
method'find'
objectstring
query{ context?: object; where?: Record<string, any> | any; fields?: string[]; orderBy?: object[]; … }optional

DataEngineInsertOptions

Options for DataEngine.insert operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
returningbooleanoptional

DataEngineInsertRequest

Properties

PropertyTypeRequiredDescription
method'insert'
objectstring
dataRecord<string, any> | Record<string, any>[]
options{ context?: object; returning?: boolean }optionalOptions for DataEngine.insert operations

DataEngineQueryOptions

Query options for IDataEngine.find() operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
filterRecord<string, any> | anyoptionalData Engine query filter conditions
selectstring[]optional
sortRecord<string, Enum<'asc' | 'desc'>> | Record<string, 1 | -1> | { field: string; order: Enum<'asc' | 'desc'> }[]optionalSort order definition
limitintegeroptional
skipintegeroptional
topintegeroptional
populatestring[]optional

DataEngineRequest

Virtual ObjectQL Request Protocol

Union Options

This schema accepts one of the following structures:

Option 1

Properties

PropertyTypeRequiredDescription
method'find'
objectstring
query{ context?: object; where?: Record<string, any> | any; fields?: string[]; orderBy?: object[]; … }optional

Option 2

Properties

PropertyTypeRequiredDescription
method'findOne'
objectstring
query{ context?: object; where?: Record<string, any> | any; fields?: string[]; orderBy?: object[]; … }optional

Option 3

Properties

PropertyTypeRequiredDescription
method'insert'
objectstring
dataRecord<string, any> | Record<string, any>[]
options{ context?: object; returning?: boolean }optionalOptions for DataEngine.insert operations

Option 4

Properties

PropertyTypeRequiredDescription
method'update'
objectstring
dataRecord<string, any>
idstring | numberoptionalID for single update, or use where in options
options{ context?: object; where?: Record<string, any> | any; multi?: boolean; returning?: boolean; … }optional

Option 5

Properties

PropertyTypeRequiredDescription
method'delete'
objectstring
idstring | numberoptionalID for single delete, or use where in options
options{ context?: object; where?: Record<string, any> | any; multi?: boolean; filter?: Record<string, any> | any }optional

Option 6

Properties

PropertyTypeRequiredDescription
method'count'
objectstring
query{ context?: object; where?: Record<string, any> | any; filter?: Record<string, any> | any }optional

Option 7

Properties

PropertyTypeRequiredDescription
method'aggregate'
objectstring
query{ context?: object; where?: Record<string, any> | any; groupBy?: (string | object)[]; aggregations?: object[]; … }

Option 8

Properties

PropertyTypeRequiredDescription
method'execute'
commandany
optionsRecord<string, any>optional

Option 9

Properties

PropertyTypeRequiredDescription
method'vectorFind'
objectstring
vectornumber[]
whereRecord<string, any> | anyoptional
fieldsstring[]optional
limitintegeroptional
thresholdnumberoptional


DataEngineSort

Sort order definition

Union Options

This schema accepts one of the following structures:

Option 1

Type: Record<string, Enum<'asc' | 'desc'>>


Option 2

Type: Record<string, 1 | -1>


Option 3

Type: { field: string; order: Enum<'asc' | 'desc'> }[]



DataEngineUpdateOptions

Options for DataEngine.update operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
filterRecord<string, any> | anyoptionalData Engine query filter conditions
upsertneveroptional[REMOVED] update.options.upsert was removed in @objectstack/spec 17 (#8057, ADR-0049) — it was declared and allowlisted but never implemented: no engine or driver path ever read it, so { upsert: true } was accepted and silently dropped and the update stayed a plain update. Delete the key. Express create-if-absent explicitly: a by-id update whose id names no row throws RECORD_NOT_FOUND (#7867's not-found gate) rather than inserting, so read the row first (findOne) and call insert or update on what you find. A first-class upsert, if ever built, must reconcile with that gate by design rather than through this silent flag.
multibooleanoptional
returningbooleanoptional

DataEngineUpdateRequest

Properties

PropertyTypeRequiredDescription
method'update'
objectstring
dataRecord<string, any>
idstring | numberoptionalID for single update, or use where in options
options{ context?: object; where?: Record<string, any> | any; multi?: boolean; returning?: boolean; … }optional

DataEngineVectorFindRequest

Properties

PropertyTypeRequiredDescription
method'vectorFind'
objectstring
vectornumber[]
whereRecord<string, any> | anyoptional
fieldsstring[]optional
limitintegeroptional
thresholdnumberoptional

DroppedFieldsEvent

A write-path strip event: caller-supplied fields legally dropped from the payload (#3407)

Properties

PropertyTypeRequiredDescription
objectstringObject the write targeted (resolved object name)
fieldsstring[]Caller-supplied field names the engine removed from the write payload
reasonEnum<'readonly' | 'readonly_when' | 'primary_key'>Why the fields were dropped: static readonly (#2948), a TRUE readonlyWhen predicate (#3042), or the primary-key strip of a payload id the engine ruled is not an identifier (#6437)

EngineAggregateOptions

QueryAST-aligned options for DataEngine.aggregate operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
whereRecord<string, any> | anyoptional
groupBy(string | { field: string; dateGranularity?: Enum<'day' | 'week' | 'month' | 'quarter' | 'year'>; alias?: string })[]optionalGROUP BY targets (strings or {field, dateGranularity?} objects for date bucketing)
aggregations{ function: Enum<'count' | 'sum' | 'avg' | 'min' | 'max' | 'count_distinct'>; field?: string; alias: string; filter?: any }[]optional
havinganyoptionalHAVING — filter over the aggregated rows (aggregation aliases + groupBy projections); applied engine-side after aggregation
timezonestringoptional

EngineCountOptions

QueryAST-aligned options for DataEngine.count operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
whereRecord<string, any> | anyoptional

EngineDeleteOptions

QueryAST-aligned options for DataEngine.delete operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
whereRecord<string, any> | anyoptional
multibooleanoptional

EngineQueryOptions

QueryAST-aligned query options for IDataEngine.find() operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
whereRecord<string, any> | anyoptional
fieldsstring[]optional
orderBy{ field: string; order: Enum<'asc' | 'desc'> }[]optional
limitnumberoptional
offsetnumberoptional
topnumberoptional
cursorneveroptional[REMOVED] query.cursor was removed in @objectstack/spec 17 (#4286, ADR-0049) — no driver ever implemented keyset pagination, so the cursor was accepted and ignored and every page came back identical (a caller looping "until hasMore is false" never terminates). Delete the key; QueryBuilder.cursor() was removed with it. Express the keyset as an ordinary where predicate on your sort key — where: { created_at: { $gt: last.created_at } } with the matching orderBy — which every driver executes with canonicalised comparands. A first-class cursor, if ever built, will be a response-minted opaque token, not this caller-built record.
searchstring | { query: string; fields?: string[]; fuzzy: boolean; operator: Enum<'and' | 'or'>; … }optional
searchFieldsstring[]optional
expandRecord<string, { object: string; fields?: string[]; where?: any; search?: string | object; … }>optional
distinctneveroptional[REMOVED] query.distinct was removed in @objectstack/spec 17 (#4286, ADR-0049 / ADR-0078) — no driver ever rendered SELECT DISTINCT; the flag's only observable effect was MIS-WIRED: the REST list path treated a distinct query as not countable and silently degraded total/hasMore to a page-local estimate while still returning duplicate rows. Delete the key; QueryBuilder.distinct() was removed with it, and the count suppression is gone (total is truthful again). For unique values of one column use the SQL/memory drivers' distinct(object, field) door; for unique combinations, groupBy; for a deduplicated count, the count_distinct aggregation.

EngineUpdateOptions

QueryAST-aligned options for DataEngine.update operations

Properties

PropertyTypeRequiredDescription
context{ userId?: string; actor?: string; attributedUserId?: string; email?: string; … }optional
whereRecord<string, any> | anyoptional
upsertneveroptional[REMOVED] update.options.upsert was removed in @objectstack/spec 17 (#8057, ADR-0049) — it was declared and allowlisted but never implemented: no engine or driver path ever read it, so { upsert: true } was accepted and silently dropped and the update stayed a plain update. Delete the key. Express create-if-absent explicitly: a by-id update whose id names no row throws RECORD_NOT_FOUND (#7867's not-found gate) rather than inserting, so read the row first (findOne) and call insert or update on what you find. A first-class upsert, if ever built, must reconcile with that gate by design rather than through this silent flag.
multibooleanoptional
returningbooleanoptional

On this page