Types
Core
DatrixEntry
interface DatrixEntry {
id: number
createdAt: Date
updatedAt: Date
}Base type every record extends. Fields are injected automatically and cannot be written manually.
IDatrix
interface IDatrix extends IRawCrud {
// CRUD (with plugin hooks) — same as IRawCrud methods
findOne(model, where, options?): Promise<T | null>
findById(model, id, options?): Promise<T | null>
findMany(model, options?): Promise<T[]>
count(model, where?): Promise<number>
create(model, data, options?): Promise<T>
createMany(model, data[], options?): Promise<T[]>
update(model, id, data, options?): Promise<T>
updateMany(model, where, data, options?): Promise<T[]>
delete(model, id, options?): Promise<T>
deleteMany(model, where, options?): Promise<T[]>
// Raw CRUD (bypasses plugin hooks)
readonly raw: IRawCrud
// Schema access
getSchemas(): ISchemaRegistry
getSchema(name): SchemaDefinition | undefined
hasSchema(name): boolean
getAllSchemas(): readonly SchemaDefinition[]
// Plugin access
getPlugins(): readonly DatrixPlugin[]
getPlugin(name): DatrixPlugin | null
hasPlugin(name): boolean
// Lifecycle
isInitialized(): boolean
getConfig(): DatrixConfig
getAdapter<T>(): T
shutdown(): Promise<void>
}Main Datrix instance interface. Returned by the function created with defineConfig(). All CRUD methods run through plugin hooks — use .raw for direct database access without hooks.
RawCrudOptions
interface RawCrudOptions<T> {
select?: SelectClause<T>
populate?: PopulateClause<T>
noReturning?: boolean
}Options for single-record operations (findOne, findById, create, update, delete).
RawFindManyOptions
interface RawFindManyOptions<T> extends RawCrudOptions<T> {
where?: WhereClause<T>
orderBy?: OrderByClause<T>
limit?: number
offset?: number
}Options for findMany. Extends RawCrudOptions with filtering and pagination.
Query
WhereClause
type WhereClause<T> =
| { [K in keyof T]?: ComparisonOperators<T[K]> | T[K] }
| { $and: WhereClause<T>[] }
| { $or: WhereClause<T>[] }
| { $not: WhereClause<T> }Filter expression. Supports direct values, comparison operators ($eq, $gt, $in…), logical operators ($and, $or, $not), and nested relation conditions.
SelectClause
type SelectClause<T> =
| (keyof T)[]
| keyof T
| "*"Fields to return. Use "*" for all fields. Relation fields cannot appear here — use populate instead.
PopulateClause
type PopulateClause<T> =
| true
| "*"
| string[]
| { [relation: string]: true | PopulateOptions }Relations to load alongside the main record. Supports true, "*", array of names, or object with per-relation options.
PopulateOptions
type PopulateOptions<T> = {
select?: SelectClause<T>
where?: WhereClause<T>
populate?: PopulateClause<T>
limit?: number
offset?: number
orderBy?: OrderByClause<T>
}Per-relation populate options. Used as the value in an object-form PopulateClause.
OrderByClause
type OrderByClause<T> =
| { field: keyof T; direction: "asc" | "desc"; nulls?: "first" | "last" }[]
| { [K in keyof T]?: "asc" | "desc" }
| string[]Sort order. Three formats: full object array, shorthand object, or string array ("-field" for desc).
Schema
SchemaDefinition
interface SchemaDefinition {
name: string
fields: Record<string, FieldDefinition>
indexes?: IndexDefinition[]
hooks?: LifecycleHooks
timestamps?: boolean
softDelete?: boolean
tableName?: string
permission?: SchemaPermission
}Defines a database table's structure, constraints, and access rules. Pass the result of defineSchema() to schemas[] in defineConfig().
FieldDefinition
type FieldDefinition =
| StringField
| NumberField
| BooleanField
| DateField
| JsonField
| EnumField
| ArrayField
| RelationField
| FileFieldDiscriminated union of all field type interfaces. The type property on each determines which options are available.
StringField
interface StringField {
type: "string"
required?: boolean
default?: string
unique?: boolean
minLength?: number
maxLength?: number
pattern?: RegExp
validator?: (value: string) => true | string
errorMessage?: string
description?: string
permission?: FieldPermission
}String field definition. Use minLength/maxLength for length constraints, pattern for regex validation, validator for custom logic.
NumberField
interface NumberField {
type: "number"
required?: boolean
default?: number
unique?: boolean
min?: number
max?: number
integer?: boolean
autoIncrement?: boolean
validator?: (value: number) => true | string
description?: string
permission?: FieldPermission
}Number field definition. Set integer: true to disallow decimals. autoIncrement is typically only used for the primary key.
BooleanField
interface BooleanField {
type: "boolean"
required?: boolean
default?: boolean
description?: string
permission?: FieldPermission
}Boolean field definition.
DateField
interface DateField {
type: "date"
required?: boolean
default?: Date
min?: Date
max?: Date
description?: string
permission?: FieldPermission
}Date field definition.
JsonField
interface JsonField {
type: "json"
required?: boolean
default?: Record<string, unknown>
schema?: Record<string, unknown> // JSON schema validation
description?: string
permission?: FieldPermission
}JSON field definition. Stored as a JSON column. Optionally validated against a JSON schema.
EnumField
interface EnumField {
type: "enum"
required?: boolean
default?: string
values: readonly string[] // allowed values
description?: string
permission?: FieldPermission
}Enum field definition. values defines the complete set of allowed string literals.
ArrayField
interface ArrayField {
type: "array"
required?: boolean
items: FieldDefinition // type of each element
minItems?: number
maxItems?: number
unique?: boolean // all items must be unique
description?: string
permission?: FieldPermission
}Array field definition. items defines the type of each element — can be any FieldDefinition including nested objects.
RelationField
interface RelationField {
type: "relation"
required?: boolean
model: string // target schema name
kind: RelationKind
foreignKey?: string // defaults to fieldName + "Id"
through?: string // join table for manyToMany
onDelete?: "cascade" | "setNull" | "restrict"
onUpdate?: "cascade" | "restrict"
description?: string
permission?: FieldPermission
}Relation field definition. kind determines the cardinality. For manyToMany, a junction table is auto-generated if through is omitted.
RelationKind
type RelationKind =
| "hasOne" // 1:1 — this model owns the FK on the other side
| "hasMany" // 1:N — other model holds the FK
| "belongsTo" // N:1 — this model holds the FK
| "manyToMany" // N:N — junction tableDefines the cardinality of a relation field.
FileField
interface FileField {
type: "file"
required?: boolean
allowedTypes?: string[] // MIME types, e.g. ["image/png"]
maxSize?: number // bytes
multiple?: boolean
description?: string
permission?: FieldPermission
}File field definition. Requires the upload plugin to be configured.
IndexDefinition
interface IndexDefinition {
name?: string
fields: readonly string[]
unique?: boolean
type?: "btree" | "hash" | "gist" | "gin"
}Defines a database index on one or more fields. Pass to indexes[] in SchemaDefinition.
LifecycleHooks
interface LifecycleHooks<T extends DatrixEntry = DatrixEntry> {
beforeCreate?: (
query: QueryInsertObject<T>,
ctx: QueryContext,
) => Promise<QueryInsertObject<T>> | QueryInsertObject<T>
afterCreate?: (
records: readonly T[],
ctx: QueryContext,
) => Promise<readonly T[]> | readonly T[]
beforeUpdate?: (
query: QueryUpdateObject<T>,
ctx: QueryContext,
) => Promise<QueryUpdateObject<T>> | QueryUpdateObject<T>
afterUpdate?: (
records: readonly T[],
ctx: QueryContext,
) => Promise<readonly T[]> | readonly T[]
beforeDelete?: (
query: QueryDeleteObject<T>,
ctx: QueryContext,
) => Promise<QueryDeleteObject<T>> | QueryDeleteObject<T>
afterDelete?: (
records: readonly T[],
ctx: QueryContext,
) => Promise<void> | void
beforeFind?: (
query: QuerySelectObject<T>,
ctx: QueryContext,
) => Promise<QuerySelectObject<T>> | QuerySelectObject<T>
afterFind?: (
records: readonly T[],
ctx: QueryContext,
) => Promise<readonly T[]> | readonly T[]
}Schema lifecycle hooks. Defined in the hooks field of SchemaDefinition. Before hooks receive the full query object and must return it. After hooks receive all affected records as an array. ctx.datrix gives access to the Datrix instance for additional queries.
Permissions
SchemaPermission
interface SchemaPermission {
create?: PermissionValue
read?: PermissionValue
update?: PermissionValue
delete?: PermissionValue
}Schema-level access control. Each action accepts true, false, a role array, a function, or a mixed array of roles and functions.
NOTE: Only enforced when using the @datrix/api package.
FieldPermission
interface FieldPermission {
read?: PermissionValue
write?: PermissionValue
}Field-level access control. read: if denied, field is stripped from the response. write: if denied, returns 403.
NOTE: Only enforced when using the @datrix/api package.
PermissionValue
type PermissionValue =
| boolean
| readonly string[] // role names
| PermissionFn // (ctx) => boolean
| readonly (string | PermissionFn)[] // role OR function (OR logic)Defines who can perform an action. true = everyone, false = nobody, string array = specific roles, function = custom logic.
NOTE: Only enforced when using the @datrix/api package.
PermissionFn
type PermissionFn = (ctx: PermissionContext) => boolean | Promise<boolean>Custom permission function. Receives the full request context and returns true to allow, false to deny.
NOTE: Only enforced when using the @datrix/api package.
PermissionContext
interface PermissionContext {
readonly user: AuthUser | undefined
readonly action: PermissionAction
readonly record?: DatrixEntry // existing record (update/delete)
readonly input?: Partial<DatrixEntry> // incoming data (create/update)
readonly id?: number | null
}Context passed to permission functions. user is undefined for unauthenticated requests.
Adapter
DatabaseAdapter
interface DatabaseAdapter<TConfig = object> {
readonly name: string
readonly config: TConfig
connect(): Promise<void>
disconnect(): Promise<void>
isConnected(): boolean
beginTransaction(): Promise<Transaction>
getTables(): Promise<readonly string[]>
tableExists(name: string): Promise<boolean>
executeQuery(query): Promise<QueryResult>
createTable(schema): Promise<void>
dropTable(name): Promise<void>
alterTable(name, ops): Promise<void>
}Interface all database adapters must implement. Passed as adapter in defineConfig().
Transaction
interface Transaction {
readonly id: string
commit(): Promise<void>
rollback(): Promise<void>
savepoint(name): Promise<void>
rollbackTo(name): Promise<void>
release(name): Promise<void>
executeQuery(query): Promise<QueryResult>
executeRawQuery(sql): Promise<QueryResult>
createTable(schema): Promise<void>
dropTable(name): Promise<void>
alterTable(name, ops): Promise<void>
}Wraps a database transaction. Supports query execution and schema operations atomically. Returned by DatabaseAdapter.beginTransaction().
Plugin
DatrixPlugin
interface DatrixPlugin<TOptions = Record<string, unknown>> {
readonly name: string
readonly version: string
readonly options: TOptions
init(context: PluginContext): Promise<void>
destroy(): Promise<void>
getSchemas?(): Promise<SchemaDefinition[]>
extendSchemas?(ctx: SchemaExtensionContext): Promise<SchemaExtension[]>
onBeforeQuery?<T>(query: QueryObject<T>, ctx: QueryContext): Promise<QueryObject<T>>
onAfterQuery?<T>(result: T, ctx: QueryContext): Promise<T>
}Interface all Datrix plugins must implement.
PluginContext
interface PluginContext {
readonly adapter: DatabaseAdapter
readonly schemas: SchemaRegistry
readonly config: DatrixConfig
}Context provided to a plugin's init() method. Gives access to the adapter, schema registry, and configuration.
QueryContext
interface QueryContext {
readonly action: QueryAction
readonly datrix: IDatrix
readonly metadata: Record<string, unknown>
user?: AuthUser
}Context passed to plugin hooks and schema lifecycle hooks. Gives access to the current action, the Datrix instance, shared metadata, and the authenticated user.
SchemaExtensionContext
interface SchemaExtensionContext {
readonly schemas: readonly SchemaDefinition[]
extendAll(modifier): SchemaExtension[]
extendWhere(predicate, modifier): SchemaExtension[]
extendByPattern(pattern, modifier): SchemaExtension[]
}Context passed to a plugin's extendSchemas() hook. Provides helpers to extend all or a subset of schemas.
SchemaExtension
interface SchemaExtension {
readonly targetSchema: string
readonly fields?: Record<string, FieldDefinition>
readonly removeFields?: string[]
readonly modifyFields?: Record<string, Partial<FieldDefinition>>
readonly indexes?: IndexDefinition[]
}Describes fields and indexes to add, remove, or modify on an existing schema. Returned from extendSchemas().
Schema registry
SchemaRegistry
class SchemaRegistry {
get(name: string): SchemaDefinition | undefined
has(name: string): boolean
getAll(): readonly SchemaDefinition[]
getNames(): readonly string[]
readonly size: number
}Registry that holds all registered schemas. Returned by getSchemas().
Migration
MigrationSession
class MigrationSession {
tablesToCreate: readonly SchemaDefinition[]
ambiguous: readonly AmbiguousChange[]
hasAmbiguous: boolean
resolveAmbiguous(id: string, action: AmbiguousActionType): void
getPlan(): MigrationPlan
apply(): Promise<readonly MigrationExecutionResult[]>
}Returned by beginMigrate(). Represents a diff session between current schemas and database state.
AmbiguousChange
interface AmbiguousChange {
readonly id: string // e.g. "user.name->lastname"
readonly description: string
readonly options: readonly AmbiguousActionType[]
}A schema change that Datrix cannot resolve automatically — typically a field rename vs. drop+add. Must be resolved with MigrationSession.resolveAmbiguous() before applying.
AmbiguousActionType
type AmbiguousActionType =
| "rename" // treat as a rename operation
| "drop" // drop the old field and add the new oneResolution for an ambiguous schema change. Passed to MigrationSession.resolveAmbiguous().
AlterOperation
type AlterOperation =
| { type: "addColumn"; column: string; definition: FieldDefinition }
| { type: "dropColumn"; column: string }
| { type: "modifyColumn"; column: string; newDefinition: FieldDefinition }
| { type: "renameColumn"; from: string; to: string }
| { type: "addMetaField"; field: string; definition: FieldDefinition }
| { type: "dropMetaField"; field: string }
| { type: "modifyMetaField"; field: string; newDefinition: FieldDefinition }Discriminated union of column-level DDL operations used in alterTable(). MetaField variants are for internal Datrix-managed columns.
Migration
interface Migration {
readonly metadata: MigrationMetadata
readonly operations: readonly MigrationOperation[]
}A single migration unit — metadata plus the list of DDL operations to execute.
MigrationMetadata
interface MigrationMetadata {
readonly name: string
readonly version: string
readonly timestamp: number
readonly description?: string
readonly author?: string
}Descriptive metadata attached to a migration. version is used to track which migrations have been applied.
MigrationOperation
type MigrationOperation =
| { type: "createTable"; schema: SchemaDefinition }
| { type: "dropTable"; tableName: string }
| { type: "alterTable"; tableName: string; operations: AlterOperation[] }
| { type: "createIndex"; tableName: string; index: IndexDefinition }
| { type: "dropIndex"; tableName: string; indexName: string }
| { type: "renameTable"; from: string; to: string }
| { type: "raw"; sql: string; params?: unknown[] }
| { type: "dataTransfer"; description: string }Discriminated union of all DDL operations a migration can contain.
MigrationStatus
type MigrationStatus =
| "pending"
| "running"
| "completed"
| "failed"Execution state of a migration.
MigrationPlan
interface MigrationPlan {
readonly migrations: readonly Migration[]
readonly target?: string
}The list of migrations to execute, as returned by MigrationSession.getPlan(). target is undefined when targeting the latest version.
MigrationExecutionResult
interface MigrationExecutionResult {
readonly migration: Migration
readonly status: MigrationStatus
readonly executionTime: number
readonly error?: Error
readonly warnings?: string[]
}Result of a single migration execution. status is one of 'pending' | 'running' | 'completed' | 'failed'.
Config
DatrixConfig
interface DatrixConfig {
adapter: DatabaseAdapter
schemas: SchemaDefinition[]
plugins?: DatrixPlugin[]
migration?: MigrationConfig
dev?: DevConfig
}Top-level configuration object passed to defineConfig().
MigrationConfig
interface MigrationConfig {
auto?: boolean // run migrations on startup
directory?: string // default: "./migrations"
modelName?: string // tracking table name
}Controls migration behavior. auto defaults to false in production.
DevConfig
interface DevConfig {
logging?: boolean // detailed query logging
validateQueries?: boolean // validate queries before execution
prettyErrors?: boolean // pretty-print errors with stack traces
}Development mode options. All options default to false in production.
API
ApiConfig
interface ApiConfig<TRole extends string = string> {
prefix?: string // route prefix — default: '/api'
defaultPageSize?: number // default: 25
maxPageSize?: number // default: 100
maxPopulateDepth?: number // default: 5
autoRoutes?: boolean // auto-generate CRUD routes — default: true
excludeSchemas?: string[] // schemas to exclude from auto-routes
auth?: AuthConfig<TRole>
upload?: IUpload
}Configuration for ApiPlugin. Pass to new ApiPlugin({ ... }).
AuthConfig
interface AuthConfig<TRole extends string = string> {
roles: readonly TRole[]
defaultRole: TRole
defaultPermission?: SchemaPermission
jwt?: JwtConfig
session?: SessionConfig
password?: PasswordConfig
authSchemaName?: string // default: 'authentication'
userSchema?: {
name?: string // default: 'user'
email?: string // default: 'email'
}
endpoints?: {
login?: string // default: '/auth/login'
register?: string // default: '/auth/register'
logout?: string // default: '/auth/logout'
me?: string // default: '/auth/me'
disableRegister?: boolean // default: false
}
}Authentication configuration block inside ApiConfig. Enables JWT and/or session auth, RBAC roles, and password policy.
JwtConfig
interface JwtConfig {
secret: string // min 32 characters
expiresIn?: string | number // e.g. "7d", "1h" or seconds
algorithm?: "HS256" | "HS512" // default: "HS256"
issuer?: string // JWT iss claim
audience?: string // JWT aud claim
}JWT signing options inside AuthConfig.
SessionConfig
interface SessionConfig {
store?: "memory" | SessionStore // default: "memory"
maxAge?: number // session lifetime in seconds — default: 86400
checkPeriod?: number // expired session cleanup interval — default: 3600
prefix?: string // session key prefix for memory store — default: "sess:"
}Session storage options inside AuthConfig. Pass a custom SessionStore instance for Redis, database, or any other backend.
PasswordConfig
interface PasswordConfig {
iterations?: number // PBKDF2 iterations — default: 100000
keyLength?: number // derived key length in bytes — default: 64
minLength?: number // minimum password length — default: 8
}Password hashing policy inside AuthConfig.
SessionStore
interface SessionStore {
get(sessionId: string): Promise<SessionData | undefined>
set(sessionId: string, data: SessionData): Promise<void>
delete(sessionId: string): Promise<void>
cleanup(): Promise<number>
clear(): Promise<void>
}Interface for custom session backends. Implement this to use Redis, a database, or any other storage instead of the default in-memory store.
ParsedQuery
interface ParsedQuery<T extends DatrixEntry = DatrixRecord> {
select?: SelectClause<T>
where?: WhereClause<T>
populate?: PopulateClause<T>
orderBy?: OrderByClause<T>
page?: number
pageSize?: number
}Typed query object accepted by queryToParams and parsed by the server. Client and server share the same shape.
Upload
UploadOptions
interface UploadOptions<TResolutions extends string = string> {
provider: StorageProvider
modelName?: string // default: "media"
maxSize?: number // max file size in bytes
allowedMimeTypes?: string[] // supports wildcards, e.g. "image/*"
format?: ImageFormat // convert all images to this format
quality?: number // 1–100 — default: 80
resolutions?: Record<TResolutions, ResolutionConfig>
permission?: SchemaPermission
}Options passed to new Upload({ ... }). Controls storage, validation, format conversion, and resolution variants.
ImageFormat
type ImageFormat = "webp" | "jpeg" | "png" | "avif"Target format for image conversion. All uploaded images are converted to this format before storage.
ResolutionConfig
interface ResolutionConfig {
width: number
height?: number // omit to preserve aspect ratio
fit?: ResizeFit
}Config for a single named resolution variant. height is optional — if omitted, aspect ratio is preserved.
ResizeFit
type ResizeFit =
| "cover" // crop to fill the box exactly
| "contain" // fit within the box, letterbox if needed
| "fill" // stretch to fill — ignores aspect ratio
| "inside" // resize so both dimensions fit inside the box
| "outside" // resize so one dimension fills the boxSharp fit mode used when both width and height are set on a ResolutionConfig.
LocalProviderOptions
interface LocalProviderOptions {
basePath: string // directory to write files into
baseUrl: string // public URL prefix
ensureDirectory?: boolean // create basePath if missing — default: true
}Options for LocalStorageProvider.
S3ProviderOptions
interface S3ProviderOptions {
bucket: string
region: string
accessKeyId: string
secretAccessKey: string
endpoint?: string // custom endpoint for R2 / MinIO
pathPrefix?: string // optional key prefix
}Options for S3StorageProvider. Compatible with AWS S3, Cloudflare R2, MinIO, and any S3-compatible storage.
StorageProvider
interface StorageProvider {
readonly name: string
upload(file: UploadFile): Promise<UploadResult>
delete(key: string): Promise<void>
getUrl(key: string): string
exists(key: string): Promise<boolean>
}Interface all storage backends must implement. Implement this to add a custom storage provider.
MediaEntry
interface MediaEntry<TResolutions extends string = string> extends DatrixEntry {
filename: string
originalName: string
mimeType: string
size: number
key: string
url: string // injected at response time, not stored in DB
variants: MediaVariants<TResolutions> | null
}Shape of a media record. key is stored in the database — url is derived at response time via the configured provider.
MediaVariant
interface MediaVariant {
key: string
url: string // injected at response time, not stored in DB
width: number
height: number
size: number
mimeType: string
}A single processed resolution variant. Available in MediaEntry.variants under the resolution name.