InternalA new (unpersisted) instance of the provided dream class
InternalReturns an array of association names for the given dream class
new Post().associationNames
// [ 'user', 'postVisibility', 'ratings', 'heartRatings' ]
association names array
Returns an object with column names for keys, and an array of strings representing validation errors for values.
An error object
Returns true if any of the attributes on the instance have changed since it was last pulled from the database. It will also return true if the instance is not yet persisted.
const post = Post.new({ body: 'howyadoin' })
post.isDirty
// true
await post.save()
post.isDirty
// false
post.body = 'groiyyyt'
post.isDirty
// true
A boolean
Runs validation checks against all validations declared using the Validate and Validates decorators, and returns true if any of them fail.
class User extends ApplicationModel {
@deco.Validates('presence')
public email: DreamColumn<User, 'email'>
}
const user = User.new()
user.isInvalid
// true
user.email = 'how@yadoin'
user.isInvalid
// false
A boolean
Returns true if the model has not been persisted to the database.
const user = User.new({ email: 'howyadoin' })
user.isNewRecord
// true
await user.save()
user.isNewRecord
// false
A boolean
Returns true if the model has been persisted to the database.
const user = User.new({ email: 'howyadoin' })
user.isPersisted
// false
await user.save()
user.isPersisted
// true
A boolean
Runs validation checks against all validations declared using the Validate and Validates decorators. Returns true if none of the validations fail.
NOTE: Any validations that fail will leave the errors field populated on your model instance.
class User extends ApplicationModel {
@deco.Validates('presence')
public email: DreamColumn<User, 'email'>
}
const user = User.new()
user.isValid
// false
user.email = 'how@yadoin'
user.isValid
// true
A boolean
Returns the string value representing this model type that will be stored in the database for STI children or polymorphic associations.
This value is used to identify the specific model class when querying polymorphic associations or STI records. For STI models, this returns the base class' sanitized name. For regular models, it returns the model's sanitized class name.
When building queries manually, use this method for the type or polymorphic type part of the query. For example:
// Using in polymorphic queries
const localizedTexts = await LocalizedText.passthrough({ locale })
.whereAny(
modelsWithTextAssociation.map(
model => ({
localizableId: model.primaryKeyValue(),
localizableType: model.referenceTypeString,
})
)
)
.all()
// Example values for different model types
const user = User.new()
user.referenceTypeString // 'User'
// For STI child classes, returns the base class name
const mylarBalloon = MylarBalloon.new() // extends Balloon (STI base)
mylarBalloon.referenceTypeString // 'Balloon'
The string identifier for this model type used in database storage
this.constructor.name may be prefixed with an underscore during conversion to Javascript. This method returns the constructor name without a leading underscore.
A string
This must be defined on every model, and it must point
to a table in your database. In addition, you must
return the value using the as const suffix, like so:
class User extends ApplicationModel {
public override get table() {
return 'users' as const
}
}
The table name for this model
StaticassociationReturns all of the association names for this Dream class. This includes all BelongsTo, HasOne, and HasMany associations defined on the model.
This is useful for introspection, debugging, or dynamically working with all associations on a model.
class User extends ApplicationModel {
@deco.HasMany('Post')
public posts: Post[]
@deco.HasOne('Profile')
public profile: Profile
@deco.BelongsTo('Company')
public company: Company
}
User.associationNames
// ['posts', 'profile', 'company']
})
An array containing all association names defined on this Dream class
StaticglobalInternalReturns a unique global name for the given model.
A string representing a unique key for this model
StaticisInternalProvided to distinguish between Dream and other classes
true
StaticprimaryShadows #primaryKey, a getter which can be overwritten to customize the id field for a given model.
string
StaticreferenceReturns the string value representing this model type that will be stored in the database for STI children or polymorphic associations.
This value is used to identify the specific model class when querying polymorphic associations or STI records. For STI models, this returns the base class' sanitized name. For regular models, it returns the model's sanitized class name.
When building queries manually, use this method for the type or polymorphic type part of the query. For example:
// Using in polymorphic queries
const localizedTexts = await LocalizedText.passthrough({ locale })
.whereAny(
modelsWithTextAssociation.map(
model => ({
localizableId: model.primaryKeyValue(),
localizableType: model.referenceTypeString,
})
)
)
.all()
// Example values for different model types
const user = User.new()
user.referenceTypeString // 'User'
// For STI child classes, returns the base class name
const mylarBalloon = MylarBalloon.new() // extends Balloon (STI base)
mylarBalloon.referenceTypeString // 'Balloon'
The string identifier for this model type used in database storage
StaticsanitizedInternalReturns the class name, replacing prefixed underscores, since esbuild will translate some class names to have underscore prefixes, which can cause unexpected behavior.
see https://github.com/evanw/esbuild/issues/1260 for more information
string
StatictableShadows #table, a getter which can be overwritten to customize the table field for a given model.
string
Adds an error to the model. Any errors added to the model will cause the model to be invalid, and will prevent the model from saving.
class User extends ApplicationModel {
...
@deco.Validate()
public async validateName() {
if (typeof this.name === 'number')
this.addError('name', 'name cannot be a number')
}
}
void
Changes the attribute value for a single attribute, leveraging any custom-defined setters. If you would like to bypass custom-defined setters, use #setAttribute instead
const user = new User()
user.assignAttribute('email', 'sally@gmail.com')
Takes the attributes passed in and sets their values, leveraging any custom setters defined for these attributes.
NOTE:
To bypass custom-defined setters, use #setAttributes instead.
const user = new User()
user.assignAttributes({ email: 'my@email', password: 'my password' })
If the association is already loaded on the instance, it returns the loaded value immediately without making a database query. If the association is not loaded, it performs a database query to fetch the association.
If a query is performed, the association is set on the model so that future calls to access this association can access the already loaded value.
For HasMany associations, returns an array (empty if no records). For BelongsTo/HasOne associations, returns the associated model or null if not found.
The name of the BelongsTo, HasOne, or HasMany association
Optional configuration for required and passthrough clauses
The associated model instance(s) or null/empty array if not found
Warning: Using association in loops or when processing multiple records can lead to the
N+1 query problem.
Each call to association on an unloaded association will trigger a separate database query.
To avoid this, use load or preload to eager load
associations before accessing them.
// Basic association loading
const user = await post.association('user')
const comments = await post.association('comments')
// With required clause (for associations with DreamConst.required `and` clauses)
const pets = await user.association('petsWithRequiredName', {
required: { name: 'Woodstock' }
})
// With passthrough clause (for associations with DreamConst.passthrough `and` clauses)
const pets = await user.association('petsWithPassthroughName', {
passthrough: { name: 'Woodstock' }
})
// With both required and passthrough clauses (for associations with DreamConst.required and DreamConst.passthrough `and` clauses)
const text = await composition.association('passthroughAndRequiredCurrentLocalizedText', {
passthrough: { name: 'My Composition' },
required: { locale: 'de-DE' }
})
If the association is already loaded on the instance, it returns the loaded value immediately without making a database query. If the association is not loaded, it performs a database query to fetch the association.
If a query is performed, the association is set on the model so that future calls to access this association can access the already loaded value.
For HasMany associations, returns an array (empty if no records). For BelongsTo/HasOne associations, returns the associated model or null if not found.
The name of the BelongsTo, HasOne, or HasMany association
The associated model instance(s) or null/empty array if not found
Warning: Using association in loops or when processing multiple records can lead to the
N+1 query problem.
Each call to association on an unloaded association will trigger a separate database query.
To avoid this, use load or preload to eager load
associations before accessing them.
// Basic association loading
const user = await post.association('user')
const comments = await post.association('comments')
// With required clause (for associations with DreamConst.required `and` clauses)
const pets = await user.association('petsWithRequiredName', {
required: { name: 'Woodstock' }
})
// With passthrough clause (for associations with DreamConst.passthrough `and` clauses)
const pets = await user.association('petsWithPassthroughName', {
passthrough: { name: 'Woodstock' }
})
// With both required and passthrough clauses (for associations with DreamConst.required and DreamConst.passthrough `and` clauses)
const text = await composition.association('passthroughAndRequiredCurrentLocalizedText', {
passthrough: { name: 'My Composition' },
required: { locale: 'de-DE' }
})
If the association is already loaded on the instance, it returns the loaded value immediately without making a database query. If the association is not loaded, it performs a database query to fetch the association.
Unlike association, this method throws an exception if no associated
record is found for BelongsTo/HasOne associations, guaranteeing a non-null result.
For HasMany associations, returns an empty array instead of throwing when no records exist.
If a query is performed, the association is set on the model so that future calls to access this association can access the already loaded value.
For HasMany associations, returns an array (empty if no records). For BelongsTo/HasOne associations, returns the associated model or throws RecordNotFound if not found.
The name of the BelongsTo, HasOne, or HasMany association
Optional configuration for required and passthrough clauses
The associated model instance(s) (never null for BelongsTo/HasOne)
Warning: Using associationOrFail in loops or when processing multiple records can lead to the
N+1 query problem.
Each call to associationOrFail on an unloaded association will trigger a separate database query.
To avoid this, use load or preload to eager load
associations before accessing them.
// Basic association loading - throws RecordNotFound if user doesn't exist
const user = await post.associationOrFail('user')
// HasMany - returns empty array if no comments, never throws
const comments = await post.associationOrFail('comments')
// With required clause (for associations with DreamConst.required `and` clauses)
const pets = await user.associationOrFail('petsWithRequiredName', {
required: { name: 'Woodstock' }
})
// With passthrough clause (for associations with DreamConst.passthrough `and` clauses)
const pets = await user.associationOrFail('petsWithPassthroughName', {
passthrough: { name: 'Woodstock' }
})
// With both required and passthrough clauses (for associations with DreamConst.required and DreamConst.passthrough `and` clauses)
const text = await composition.associationOrFail('passthroughAndRequiredCurrentLocalizedText', {
passthrough: { name: 'My Composition' },
required: { locale: 'de-DE' }
})
If the association is already loaded on the instance, it returns the loaded value immediately without making a database query. If the association is not loaded, it performs a database query to fetch the association.
Unlike association, this method throws an exception if no associated
record is found for BelongsTo/HasOne associations, guaranteeing a non-null result.
For HasMany associations, returns an empty array instead of throwing when no records exist.
If a query is performed, the association is set on the model so that future calls to access this association can access the already loaded value.
For HasMany associations, returns an array (empty if no records). For BelongsTo/HasOne associations, returns the associated model or throws RecordNotFound if not found.
The name of the BelongsTo, HasOne, or HasMany association
The associated model instance(s) (never null for BelongsTo/HasOne)
Warning: Using associationOrFail in loops or when processing multiple records can lead to the
N+1 query problem.
Each call to associationOrFail on an unloaded association will trigger a separate database query.
To avoid this, use load or preload to eager load
associations before accessing them.
// Basic association loading - throws RecordNotFound if user doesn't exist
const user = await post.associationOrFail('user')
// HasMany - returns empty array if no comments, never throws
const comments = await post.associationOrFail('comments')
// With required clause (for associations with DreamConst.required `and` clauses)
const pets = await user.associationOrFail('petsWithRequiredName', {
required: { name: 'Woodstock' }
})
// With passthrough clause (for associations with DreamConst.passthrough `and` clauses)
const pets = await user.associationOrFail('petsWithPassthroughName', {
passthrough: { name: 'Woodstock' }
})
// With both required and passthrough clauses (for associations with DreamConst.required and DreamConst.passthrough `and` clauses)
const text = await composition.associationOrFail('passthroughAndRequiredCurrentLocalizedText', {
passthrough: { name: 'My Composition' },
required: { locale: 'de-DE' }
})
OptionaljoinAndStatements: {Returns the attributes that have changed since being persisted to the database, with the values that were last persisted to the database.
const user = User.new({ email: 'original@email', password: 'howyadoin' })
await user.save()
user.email = 'new@email'
user.changedAttributes()
// { email: 'original@email' }
user.email = 'original@email'
user.changedAttributes()
// {}
An object containing changed attributes and their original values
Returns an object containing the attributes that have changed since last persisting, along with their current and previously persisted values.
const pet = Pet.new({ species: 'dog' })
pet.changes()
// {
// species: {
// was: undefined,
// now: 'dog',
// }
// }
await pet.save()
pet.changes()
// {
// species: {
// was: undefined,
// now: 'dog',
// }
// }
pet.species = 'cat'
pet.species = 'frog'
pet.changes()
// {
// species: {
// was: 'dog',
// now: 'frog',
// }
// }
await pet.save()
pet.changes()
// {
// species: {
// was: 'dog',
// now: 'frog',
// }
// }
An object containing changed attributes
Creates an association for an instance. Automatically handles setting foreign key and, in the case of polymorphism, foreign key type.
await user.createAssociation('posts', { body: 'hello world' })
the name of the association to create
the attributes with which to create the associated model
The created association
Deletes the record represented by this instance from the database, calling any destroy hooks on this model.
const user = await User.last()
await user.destroy()
Options for destroying the instance
If true, skips applying model hooks during the destroy operation. Defaults to false
If false, skips destroying associations marked dependent: 'destroy'. Defaults to true
If true, bypasses all default scopes when cascade destroying. Defaults to false
An array of default scope names to bypass when cascade destroying. Defaults to an empty array
The instance that was destroyed
Optionaloptions: DestroyOptions<I> & {Returns an object containing the column names of columns that have changed since last persist, and their current values.
const user = User.new({ email: 'hello@world' })
user.dirtyAttributes()
// { email: 'hello@world' }
await user.save()
user.email = 'hello@world'
user.dirtyAttributes()
// {}
user.email = 'goodbye@world'
user.dirtyAttributes()
// { email: 'goodbye@world' }
user.email = 'hello@world'
user.dirtyAttributes()
// {}
An object containing the changed attributes
Returns true if the argument is the same Dream class with the same primary key value.
NOTE: This does not compare attribute values other than the primary key.
A boolean
Returns the value for a columnName provided, bypassing the getters.
const user = User.new({ email: 'how@yadoin' })
user.getAttribute('email') // 'how@yadoin'
Returns an object containing all the columns on this dream class, as well as their values, bypassing getters.
const user = User.new({ email: 'how@yadoin' })
user.getAttributes()
// {
// email: 'how@yadoin',
// ...
// }
Checks whether the specified association has been defined on this Dream model.
const user = User.new()
user.hasAssociation('posts')
// true (if User has a posts association defined)
user.hasAssociation('nonExistentAssociation')
// false
// Useful for conditional association handling
if (user.hasAssociation('posts')) {
user = await user.load('posts').execute()
}
The name of the association to check for
true if the association exists on this model, false otherwise
Returns a new Query instance with the provided inner join statement attached
await user.innerJoin('posts').first()
A Query for this model with the inner join clause applied
Returns a new Query instance with the provided left join statement attached
await user.leftJoin('posts').first()
A Query for this model with the left join clause applied
Load each specified association using a single SQL query. See Dream.load for loading in separate queries.
IMPORTANT: Like load(), leftJoinLoad().execute() returns a new clone of the
model with associations loaded. It does NOT modify the original instance.
Note: since leftJoinLoad loads via single query, it has some downsides that may be avoided using Dream.load:
limit and offset will be automatically removedthrough associations will bring additional namespaces into the query that can conflict with through associations from other associations, creating an invalid query.leftJoinLoad('a', 'b', 'c'), if each a has 10 b and each b has 10 c, then for one a, 100 records will be returned, each of which has all of the columns of a. .load('a', 'b', 'c') would perform three separate SQL queries, but the data for a single a would only be returned once..load(...).findEach(...) avoids instantiating massive amounts of data at onceconst user = await user
.leftJoinLoad('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
.leftJoinLoad('images')
.execute()
user.posts[0].comments[0].replies[0]
// Reply{}
user.images[0]
// Image{}
A chainable LeftJoinLoadBuilder instance. Call .execute() to get the cloned model with associations loaded.
Loads the requested associations upon execution.
IMPORTANT: load().execute() returns a new clone of the model with the
associations loaded. It does NOT modify the original instance. You must use the
returned value:
// CORRECT — use the returned clone:
const loaded = await user.load('posts').execute()
loaded.posts // works
// WRONG — original is not modified:
await user.load('posts').execute()
user.posts // association not loaded!
NOTE: Dream.preload is often a preferrable way of achieving the
same goal. Alternatively, await model.association('posts') loads only
if the association is not already loaded.
const user = await user
.load('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
.load('images')
.execute()
user.posts[0].comments[0].replies[0]
// Reply{}
user.images[0]
// Image{}
A chainable LoadBuilder instance. Call .execute() to get the cloned model with associations loaded.
Returns true if the association specified has been loaded on this instance
Since accessing associations that haven't been
loaded/preloaded will raise an exception, loaded
is useful for conditionally loading from the database
in contexts where an association may not yet be loaded.
user.loaded('posts')
// false
user = await user.load('posts').execute()
user.loaded('posts')
// true
the association name you wish to check the loading of
A boolean
Recursively loads all Dream associations referenced by rendersOne and rendersMany
in a DreamSerializer. This traverses the entire content tree of serializers to automatically
load all necessary associations, eliminating N+1 query problems and removing the need to
manually remember which associations to preload for serialization.
This method decouples data loading code from data rendering code by having the serializer
(rendering code) inform the query (loading code) about which associations are needed.
As serializers evolve over time - adding new rendersOne and rendersMany calls or
modifying existing ones - the loading code automatically adapts without requiring
corresponding modifications to preload statements.
This method analyzes the serializer (specified by serializerKey or 'default') and
automatically preloads all associations that will be needed during serialization.
// Instead of manually specifying all associations:
await User.preload('posts', 'comments', 'replies').all()
// Automatically preload everything needed for serialization:
await user.loadFor('summary').execute()
// Add where conditions to specific associations during preloading:
await user.loadFor('detailed', (associationName, dreamClass) => {
if (dreamClass.typeof(Post) && associationName === 'comments') {
return { and: { published: true } }
}
})
.execute()
// Skip preloading specific associations to handle them manually:
await user
.loadFor('summary', (associationName, dreamClass) => {
if (dreamClass.typeof(User) && associationName === 'posts') {
return 'omit' // Handle posts preloading separately with custom logic
}
})
.load('posts', { and: { featured: true } }) // Custom preloading
.execute()
The serializer key to use for determining which associations to preload.
OptionalmodifierFn: LoadForModifierFnOptional callback function to modify or omit specific associations during preloading. Called for each association with the Dream class and association name. Return an object with and, andAny, or andNot properties to add where conditions, return 'omit' to skip preloading that association (useful when you want to handle it manually), or return undefined to use default preloading
A Query with all serialization associations preloaded
Sends data through for use as passthrough data for the associations that require it.
class Post {
@deco.HasMany('LocalizedText')
public localizedTexts: LocalizedText[]
@deco.HasOne('LocalizedText', {
where: { locale: DreamConst.passthrough },
})
public currentLocalizedText: LocalizedText
}
await User.passthrough({ locale: 'es-ES' })
.preload('posts', 'currentLocalizedText')
.first()
where statement used for associations that require passthrough data
A cloned Query with the passthrough data
Flags a dream model so that it does not actually destroy when in a destroy phase. This is usually used for a soft-delete pattern
class User extends ApplicationModel {
@deco.BeforeDestroy()
public softDelete() {
await this.update({ deletedAt: DateTime.now() })
this.preventDeletion()
}
}
void
Returns the value most recently persisted to the database.
const pet = Pet.new({ species: 'cat' })
pet.previousValueForAttribute('species')
// undefined
await pet.save()
pet.previousValueForAttribute('species')
// undefined
pet.species = 'dog'
pet.previousValueForAttribute('species')
// 'cat'
await pet.save()
pet.previousValueForAttribute('species')
// 'cat'
await pet.update({ species: 'cat' })
pet.previousValueForAttribute('species')
// 'dog'
The column name you want the previous value for
Returns the previous value for an attribute
Returns a new instance of Query scoped to the given model class. Especially useful with Query#toKysely.
await balloon.query().toKysely('update')
.set({
multicolor: sql`array_append(multicolor, 'red')`,
})
.execute()
A new Query instance scoped to this Dream class
Deletes the record represented by this instance from the database, calling any destroy hooks on this model.
If the record being destroyed is using a SoftDelete decorator, the soft delete will be bypassed, causing the record to be permanently removed from the database.
const user = await User.last()
await user.reallyDestroy()
Options for destroying the instance
If true, skips applying model hooks during the destroy operation. Defaults to false
If false, skips destroying associations marked dependent: 'destroy'. Defaults to true
If true, bypasses all default scopes when cascade destroying. Defaults to false
An array of default scope names to bypass when cascade destroying. Defaults to an empty array
The instance that was destroyed
Optionaloptions: DestroyOptions<I> & {Saves the state of the current instance to the
database. For new instances (that have not been
persisted), save and create model hooks will be called.
For instances that have already been persisted,
the save and update model hooks will be called.
Upon saving a new instance, the primary key, timestamp
fields (if defined), and type (if STI) will be set on
the model.
Upon updating an instance, the update timestamp (if defined) will be updated on the model.
const user = User.new({ email: 'how@yadoin' })
user.name = 'fred'
await user.save()
user.email // 'how@yadoin'
user.name // 'fred'
user.id // 1
user.createdAt // A DateTime object
user.updatedAt // A DateTime object
// If User were STI:
user.type // 'User'
optional parameters
OptionalskipHooks?: booleanif true, will skip applying model hooks. Defaults to false
void
Changes the attribute value for a single attribute internally, bypassing any custom-defined setters. If you would like to set attributes without bypassing custom-defined setters, use #assignAttribute instead
const user = new User()
user.setAttribute('email', 'sally@gmail.com')
Takes the attributes passed in and sets their values internally, bypassing any custom setters defined for these attributes.
NOTE:
To leverage custom-defined setters, use #assignAttributes instead.
const user = new User()
user.setAttributes({ email: 'my@email', password: 'my password' })
Applies transaction to a new Query instance
await ApplicationModel.transaction(async txn => {
const user = await User.txn(txn).create({ email: 'how@yadoin' })
await Pet.txn(txn).create({ user })
})
A DreamTransaction instance (collected by calling ApplicationModel.transaction)
A Query scoped to this model with the transaction applied
Undestroys a SoftDelete model, unsetting
the deletedAt field in the database.
If the model is not a SoftDelete model, this will raise an exception.
const user = await User.removeAllDefaultScopes().last()
await user.undestroy()
Options for undestroying the instance
If true, skips applying model hooks during the undestroy operation. Defaults to false
If false, skips undestroying associations marked dependent: 'destroy'. Defaults to true
If true, bypasses all default scopes when cascade undestroying. Defaults to false
An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
The undestroyed record
Optionaloptions: DestroyOptions<I> & {Flags a dream model so that it allows deletion once again.
Undoes Dream.preventDeletion
class User extends ApplicationModel {
@deco.BeforeDestroy()
public async softDelete() {
await this.update({ deletedAt: DateTime.now() })
this.preventDeletion()
}
@deco.BeforeDestroy()
public async undoSoftDelete() {
await this.update({ deletedAt: null })
this.unpreventDeletion()
}
}
void
Applies all attribute changes passed to the Dream instance, leveraging any custom-defined setters, and then saves the Dream instance. Can be called on an unpersisted Dream instance.
See save for details on the side effects of saving.
NOTE: To bypass custom-defined setters, use updateAttributes instead.
const user = await User.create({ email: 'saly@gmail.com' })
await user.update({ email: 'sally@gmail.com' })
the attributes to set on the model
optional parameters
OptionalskipHooks?: booleanif true, will skip applying model hooks. Defaults to false
void
OptionalupdateAssociationOptions: {Applies all attribute changes passed to the dream, bypassing any custom-defined setters, and then saves the dream instance. Does not bypass model hooks.
See save for details on the side effects of saving.
NOTE: To update the values without bypassing any custom-defined setters, use update instead.
const user = await User.create({ email: 'saly@gmail.com' })
await user.updateAttributes({ email: 'sally@gmail.com' })
The attributes to update on this instance
optional parameters
OptionalskipHooks?: booleanif true, will skip applying model hooks. Defaults to false
StaticallRetrieves an array containing all records corresponding to
this model. Be careful using this, since it will attempt to
pull every record into memory at once. For a large number
of records, consider using .findEach, which will pull
the records in batches.
await User.all()
// [User{id: 1}, User{id: 2}...]
// only load specific columns (always includes primary key)
await User.all({ columns: ['name'] })
// Users will have both 'id' and 'name' properties
an array of dreams
StaticavgRetrieves the average for all values of the specified column for this model's records.
await Game.avg('score')
// 100
a column name on the model
the average for all values of the specified column for this model's records
StaticcolumnsStaticconnectionForces use of a database connection (e.g. 'primary') during the query.
NOTE: all queries within a transaction always use the 'primary' replica, so explicitly setting connection within a transaction has no effect.
The connection you wish to access ('primary' or 'replica')
A Query with the requested connection
StaticcountStaticcreatePersists a new record, setting the provided attributes. Automatically sets createdAt and updatedAt timestamps.
const user = await User.create({ email: 'how@yadoin', password: 'secure123' })
// Creates and saves a User with the given attributes
await Post.create({ body: 'howdy', user })
// Creates a Post with a BelongsTo association to the user
Optionalattributes: Partial<attributes or belongs to associations you wish to set on this model before persisting
optional parameters
OptionalskipHooks?: booleanif true, will skip applying model hooks. Defaults to false
A newly persisted dream instance
StaticcreateAttempt to create the model. If creation fails due to uniqueness constraint, then find the existing model.
This is useful in situations where we want to avoid a race condition creating duplicate records.
IMPORTANT: A unique index/uniqueness constraint must exist on at least one of the provided attributes
const logEntry = await LogEntry.createOrFindBy({ externalId }, { createWith: params })
The base attributes to persist, but also the attributes to use to find when create fails
Additional options
additional attributes to persist when creating, but not used for finding
if true, will skip applying model hooks. Defaults to false
A dream instance
StaticcreateAttempt to create the model with the given attributes. If creation fails due to uniqueness constraint, then find and update the existing model. All lifecycle hooks are run for whichever action is taken.
This is useful in situations where we want to avoid a race condition creating duplicate records.
IMPORTANT: A unique index/uniqueness constraint must exist on at least one of the provided attributes
The base attributes for finding which record to update, also used when creating
Additional options
additional attributes to persist when updating and creating, but not used for finding
if true, will skip applying model hooks. Defaults to false
A dream instance
StaticcursorPaginates the results of your query using cursor-based pagination, accepting a pageSize and cursor argument. This method provides better performance for large datasets by using cursor-based pagination instead of offset-based pagination.
Default order is descending primary key.
// First page (using undefined to start from beginning)
const firstPage = await User.cursorPaginate({ pageSize: 100, cursor: undefined })
firstPage.results
// [ { User{id: 777}, User{id: 776}, ...}]
firstPage.cursor
// "100" (or null if no more pages)
// Next page using cursor from previous result
const nextPage = await User.cursorPaginate({
pageSize: 100,
cursor: firstPage.cursor
})
cursor pagination options
the number of results per page
identifier of where to start the next page; use undefined to start from the beginning
A fast pagination result object containing:
cursor - identifier for the next page, or null if no more pagesresults - An array of records for the current pageStaticdistinctReturns a new query instance with the distinct query applied. If no columnName is provided, then distinct will apply to the primary key by default.
await User.distinct('name').pluck('name')
A Query scoped to this Dream model with the distinct clause applied
StaticexistsStaticfindFinds a record for the corresponding model with the specified primary key. Returns null if not found.
await User.find(123)
// User{id: 123}
await User.find(null)
// null
await User.find(undefined)
// null
Either the found record, or else null
StaticfindFinds the first record—ordered by primary key—matching the corresponding model and the specified where statement. If not found, null is returned.
await User.findBy({ email: 'how@yadoin' })
// User{email: 'how@yadoin'}
The first model found matching the whereStatement
StaticfindFinds all records for the corresponding model in batches, and then calls the provided callback for each found record. Once all records have been passed for a given batch, the next set of records will be fetched and passed to your callback, until all records matching the corresponding model have been fetched.
await User.findEach(user => {
console.log(user)
})
// User{email: 'hello@world'}
// User{email: 'goodbye@world'}
void
StaticfindAttempt to find the model with the given attributes. If no record is found, then a new record is created.
const user = await User.findOrCreateBy({ email }, { createWith: params })
The base attributes for finding, but also the attributes to use when creating
Additional options
additional attributes to persist when creating, but not used for finding
if true, will skip applying model hooks. Defaults to false
A dream instance
StaticfindFinds a record for the corresponding model with the specified primary key. If not found, an exception is raised.
await User.query().findOrFail(123)
// User{id: 123}
Either the found record, or else null
StaticfindFinds the first record—ordered by primary key—matching the corresponding model and the specified where statement. If not found, an exception is raised.
await User.findOrFailBy({ email: 'how@yadoin' })
// User{email: 'how@yadoin'}
The first model found matching the whereStatement
StaticfirstStaticfirstStaticinnerReturns a new Query instance with the provided inner join statement attached
await User.innerJoin('posts').first()
A Query for this model with the inner join clause applied
StaticisStaticisStaticlastStaticlastStaticleftReturns a new Query instance with the provided left join statement attached
await User.leftJoin('posts').first()
A Query for this model with the left join clause applied
StaticleftLoad each specified association using a single SQL query. See Dream.preload for preloading in separate queries.
Note: since leftJoinPreload loads via single query, it has some downsides and that may be avoided using Dream.preload:
limit and offset will be automatically removedthrough associations will bring additional namespaces into the query that can conflict with through associations from other associations, creating an invalid query.leftJoinPreload('a', 'b', 'c'), if each a has 10 b and each b has 10 c, then for one a, 100 records will be returned, each of which has all of the columns of a. .preload('a', 'b', 'c') would perform three separate SQL queries, but the data for a single a would only be returned once..preload(...).findEach(...) avoids instantiating massive amounts of data at onceconst user = await User.leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
console.log(user.posts[0].comments[0].replies)
// [Reply{id: 1}, Reply{id: 2}]
A query for this model with the include statement applied
StaticlimitReturns a new Query instance, specifying a limit
await User.limit(2).all()
// [User{}, User{}]
A Query for this model with the limit clause applied
Staticlookupimports an object from the IOC. This is usually done in rare cases, in order to avoid circular dependencies, since you can always use regular imports to import models or serializers otherwise.
the found model or serializer, depending on what you are looking for
StaticmaxRetrieves the max value of the specified column for this model's records.
await User.max('id')
// 99
a column name on the model
the max value of the specified column for this model's records
StaticminRetrieves the min value of the specified column for this model's records.
await User.min('id')
// 1
a column name on the model
the min value of the specified column for this model's records
StaticnewSince typescript prevents constructor functions
from absorbing type generics, we provide the new
method to instantiate with type protection
const user = User.new({ email: 'how@yadoin' })
Optionalopts: Partial<A new (unpersisted) instance of the provided dream class
StaticoffsetReturns a new Query instance, specifying an offset
await User.offset(2).order('id').limit(2).all()
// [User{id: 3}, User{id: 4}]
A Query for this model with the offset clause applied
StaticorderReturns a new Query instance, attaching the provided order statement
await User.order('id').all()
// [User{id: 1}, User{id: 2}, ...]
await User.order({ name: 'asc', id: 'desc' }).all()
// [User{name: 'a', id: 99}, User{name: 'a', id: 97}, User{ name: 'b', id: 98 } ...]
Either a string or an object specifying order. If a string, the order is implicitly ascending. If the orderStatement is an object, statements will be provided in the order of the keys set in the object
A query for this model with the order clause applied
StaticpaginatePaginates the results of your query, accepting a pageSize and page argument, which it uses to segment your query into pages, leveraging limit and offset to deliver your query to you in pages.
const paginated = await User.paginate({ pageSize: 100, page: 2 })
paginated.results
// [ { User{id: 101}, User{id: 102}, ...}]
paginated.recordCount
// 350
paginated.pageCount
// 4
paginated.currentPage
// 2
Pagination options
the page number that you want to fetch results for
the number of results per page (optional)
A paginated result object containing:
recordCount - A number representing the total number of records matching your querypageCount - The number of pages needed to encapsulate all the matching recordscurrentPage - The current page (same as what is provided in the paginate args)results - An array of records matching the current recordStaticparamReturns the list of column names that are safe for
casting automatically using .paramsFor within a
psychic controller. It will return a subset of the
.columns getter, but which filters out the following:
A subset of columns for the given dream class
StaticpassthroughSends data through for use as passthrough data for the associations that require it.
class Post {
@deco.HasMany('LocalizedText')
public localizedTexts: LocalizedText[]
@deco.HasOne('LocalizedText', {
where: { locale: DreamConst.passthrough },
})
public currentLocalizedText: LocalizedText
}
await User.passthrough({ locale: 'es-ES' })
.preload('posts', 'currentLocalizedText')
.first()
Where statement used for associations that require passthrough data
A Query for this model with the passthrough data
StaticpluckPlucks the provided fields from the corresponding model
await User.pluck('id')
// [1, 3, 2]
If more than one column is requested, a multi-dimensional array is returned:
await User.order('id').pluck('id', 'email')
// [[1, 'a@a.com'], [2, 'b@b.com']]
The column or array of columns to pluck
An array of pluck results
StaticpluckPlucks the specified fields from the given dream class table in batches, passing each found columns into the provided callback function
await User.order('id').pluckEach('id', (id) => {
console.log(id)
})
// 1
// 2
// 3
a list of fields to pluck, followed by a callback function to call for each set of found fields
void
StaticpreloadApplies preload statement to a Query scoped to this model. Upon instantiating records of this model type, specified associations will be preloaded.
Preloading/loading/including is necessary prior to accessing associations on a Dream instance.
Preload is useful for avoiding the N+1 query problem
const user = await User.preload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
console.log(user.posts[0].comments[0].replies)
// [Reply{id: 1}, Reply{id: 2}]
A query for this model with the preload statement applied
StaticpreloadRecursively preloads all Dream associations referenced by rendersOne and rendersMany
in a DreamSerializer. This traverses the entire content tree of serializers to automatically
load all necessary associations, eliminating N+1 query problems and removing the need to
manually remember which associations to preload for serialization.
This method decouples data loading code from data rendering code by having the serializer
(rendering code) inform the query (loading code) about which associations are needed.
As serializers evolve over time - adding new rendersOne and rendersMany calls or
modifying existing ones - the loading code automatically adapts without requiring
corresponding modifications to preload statements.
This method analyzes the serializer (specified by serializerKey or 'default') and
automatically preloads all associations that will be needed during serialization.
// Instead of manually specifying all associations:
await User.preload('posts', 'comments', 'replies').all()
// Automatically preload everything needed for serialization:
await User.preloadFor('summary').all()
// Add where conditions to specific associations during preloading:
await User.preloadFor('default', (associationName, dreamClass) => {
if (dreamClass.typeof(Post) && associationName === 'comments') {
return { and: { published: true } }
}
}).all()
// Skip preloading specific associations to handle them manually:
await User.preloadFor('summary', (associationName, dreamClass) => {
if (dreamClass.typeof(User) && associationName === 'posts') {
return 'omit' // Handle posts preloading separately with custom logic
}
})
.preload('posts', { and: { featured: true } }) // Custom preloading
.all()
The serializer key to use for determining which associations to preload.
OptionalmodifierFn: LoadForModifierFnOptional callback function to modify or omit specific associations during preloading. Called for each association with the Dream class and association name. Return an object with and, andAny, or andNot properties to add where conditions, return 'omit' to skip preloading that association (useful when you want to handle it manually), or return undefined to use default preloading
A Query with all serialization associations preloaded
StaticqueryReturns a new instance of Query scoped to the given model class. Especially useful with Query#toKysely.
await Balloon.query().toKysely('update').
.set({
multicolor: sql`array_remove(multicolor, ${colorToRemoveFromAllBalloons})`,
})
.execute()
A new Query instance scoped to this Dream class
StaticremoveStaticremoveStaticresortUsed in conjunction with the Sortable decorator, resort
takes a list of sortable fields, and for each one, finds and
sorts each record in the DB matching the field based on the
scope provided for each Sortable decorator found.
Calling this method shouldn't be necessary, but if
the contents of the database have shifted without firing the
correct callback mechanisms at the application layer, calling
resort will ensure that all sortable fields are set from 1..n
with no gaps, accounting for the scopes specified in the
corresponding Sortable decorator.
class Post extends ApplicationModel {
@deco.Sortable({ scope: ['user']})
public position: DreamColumn<User, 'position'>
}
await Post.all()
// [Post{position: 1}, Post{position: 3}, Post{position: 5}]
await Post.resort('position')
await Post.all()
// [Post{position: 1}, Post{position: 2}, Post{position: 3}]
void
StaticscopeReturns a Query scoped to this model with the specified scope applied.
class User extends ApplicationModel {
@deco.Scope()
public visible(query: Query<User>) {
return query.where({ hidden: false })
}
}
await User.scope('visible').all()
// [User{hidden: false}, User{hidden: false}]
a Query scoped to this model with the specified scope applied
Staticscrollscroll pagination options
the number of results per page
identifier of where to start the next page; use undefined to start from the beginning
A fast pagination result object containing:
cursor - identifier for the next page, or null if no more pagesresults - An array of records for the current pageUse cursorPaginate instead.
Paginates the results of your query using cursor-based pagination, accepting a pageSize and cursor argument. This method provides better performance for large datasets by using cursor-based pagination instead of offset-based pagination.
Default order is ascending primary key.
// First page (using undefined to start from beginning)
const firstPage = await User.scrollPaginate({ pageSize: 100, cursor: undefined })
firstPage.results
// [ { User{id: 1}, User{id: 2}, ...}]
firstPage.cursor
// "100" (or null if no more pages)
// Next page using cursor from previous result
const nextPage = await User.scrollPaginate({
pageSize: 100,
cursor: firstPage.cursor
})
StaticsqlReturns the sql that would be executed by a Query scoped to this model.
User.sql()
// {
// query: {
// kind: 'SelectQueryNode',
// from: { kind: 'FromNode', froms: [Array] },
// selections: [ [Object] ],
// distinctOn: undefined,
// joins: undefined,
// groupBy: undefined,
// orderBy: undefined,
// where: { kind: 'WhereNode', where: [Object] },
// frontModifiers: undefined,
// endModifiers: undefined,
// limit: undefined,
// offset: undefined,
// with: undefined,
// having: undefined,
// explain: undefined,
// setOperations: undefined
// },
// sql: 'select "users".* from "users" where "users"."deleted_at" is null',
// parameters: []
//}
An object representing the underlying sql statement
StaticsumRetrieves the sum for all values of the specified column for this model's records.
await Game.sum('score')
// 100
a column name on the model
the sum for all values of the specified column for this model's records
StatictoConverts the given Dream class into a Kysely query, enabling you to build custom queries using the Kysely API
await User.toKysely('select').where('email', '=', 'how@yadoin').execute()
A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, UpdateQueryBuilder, or an InsertQueryBuilder
StatictransactionBuilds a new DreamTransaction instance, provides the instance to the provided callback.
await ApplicationModel.transaction(async txn => {
const user = await User.txn(txn).create({ email: 'how@yadoin' })
await Pet.txn(txn).create({ user })
})
void
StatictxnApplies transaction to a new Query scoped to this model
await ApplicationModel.transaction(async txn => {
await User.txn(txn).create({ email: 'how@yadoin' })
})
A DreamTransaction instance (usually collected by calling ApplicationModel.transaction)
A Query scoped to this model with the transaction applied
StatictypeofDetermines if the provided Dream class is the same as or a subclass of this Dream class. This method is particularly useful for runtime type checking and works with STI (Single Table Inheritance) classes.
For regular Dream classes, this checks if the provided class is exactly the same class. For STI classes, this checks inheritance relationships - a child STI class will return true when compared against its parent class.
// Regular class comparison
User.typeof(User) // true
User.typeof(Pet) // false
// STI inheritance checking
class Balloon extends ApplicationModel {
// base STI class
}
class Mylar extends Balloon {
// STI child class
}
Balloon.typeof(Balloon) // true
Balloon.typeof(Mylar) // false
Mylar.typeof(Balloon) // true (child recognizes parent)
Mylar.typeof(Mylar) // true
// Runtime type checking with variables
const dreamClass: typeof Dream = getRandomDreamClass()
if (dreamClass.typeof(Pet)) {
// dreamClass is Pet or a subclass of Pet
}
The Dream class to compare against this class
true if the provided class is the same as this class or if this class is a subclass of the provided class (STI inheritance), false otherwise
StaticupdateAttempt to update the model with the given attributes. If no record is found, then a new record is created. All lifecycle hooks are run for whichever action is taken.
const user = await User.updateOrCreateBy({ email }, { with: { name: 'Alice' })
The base attributes for finding which record to update, also used when creating
Additional options
additional attributes to persist when updating and creating, but not used for finding
if true, will skip applying model hooks. Defaults to false
A dream instance
StaticwhereStaticwhereApplies "OR"'d where statements to a Query scoped to this model.
await User.whereAny([{ email: 'how@yadoin' }, { name: 'fred' }]).first()
// [User{email: 'how@yadoin'}, User{name: 'fred'}, User{name: 'fred'}]
a list of where statements to OR together
A Query for this model with the whereAny clause applied
Staticwhere
NOTE: avoid using the constructor function directly. Use the static
.newor.createmethods instead, which will provide type guarding for your attributes.Since typescript prevents constructor functions from absorbing type generics, we provide the
newmethod to instantiate with type protection.