Skip to main content

overview

Dream, which can be found at https://github.com/rvohealth/dream, is a custom ORM that provides the heartbeat of the framework. Psychic was built around and alongside this ORM, with the intention of providing powerful web bindings to couple with the ORM and make developing backend or fullstack applications absolutely seamless.

Dream implements the Active Record pattern to tie database rows to Typescript models and provides abstractions by which a model represents relationships to other models. Dream is a key part of the model layer in a Model View Controller (MVC) architecture.

Dream is deeply indebted to Ruby on Rails’ ActiveRecord. If you are familiar with Rails, you will feel right at home with the philosophy and developer ergonomics provided by Dream—you’ll even recognize many of the method names—but you’ll be pleasantly surprised by the power of the types to both inform development and provide build-time protection against regressions.

Model Definition

export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('users')
.addColumn('id', 'bigserial', col => col.primaryKey())
.addColumn('email', 'varchar(64)')
.addColumn('created_at', 'timestamp', col => col.notNull())
.addColumn('updated_at', 'timestamp', col => col.notNull())
.execute()
}

class User extends ApplicationModel {
public id: DreamColumn<User, 'id'> // string
public email: DreamColumn<User, 'email'> // string | null
public createdAt: DreamColumn<User, 'createdAt'> // DateTime
public updatedAt: DreamColumn<User, 'updatedAt'> // DateTime
}

With migrations run, and the above model in place, you can begin leveraging some of the powerful methods built into Dream to simplify your interactions with the database:

const user = await User.create({ email: 'how@yadoin' })
user.email // 'how@yadoin'

await User.all()
// [User{ email: 'how@yadoin' }]

Generators

Generators are the recommended way to add a new Dream model to your application. The CLI generators provide sophisticated customization to generate code that is actually useful in a maintainable, production application. To learn more about generators, see the generating guide.

Querying records

Searching your database has never been so intuitive using the suite of Dream tools provided:

// find many records by condition
const users = await User.where({ email: null }).whereNot({ name: null }).order('createdAt').all()

// find first record, or null if not found
const user = await User.order('createdAt').first()

// find first record, or raise exception. Psychic will
// automatically raise a 404 if this exception is thrown.
const user = await User.order('createdAt').firstOrFail()

To learn how to query records, see the querying guide

Creating new records

Tap into the underlying power of Dream to insert new records into your database:

// create in one call
await User.create({ email: 'how@yadoin' })

// build and save
const user = User.new({ email: 'how@yadoin' })
if (something) {
user.name = 'chalupajoe'
}
await user.save()

// attempts to create, but if it runs into a foreign key
// violation, it returns the violating record instead.
// requires a unique index on the column you are looking up
await User.createOrFindBy(
{
email: 'how@yadoin',
},
{
createWith: { name: 'chalupajoe' },
}
)

To learn how to create new records, see the creating guide

Updating existing records

With records that are already in the database, making changes to that data is made trivial by Dream:

// in a single call
await user.update({ email: 'how@yadoin' })

// build and save
if (something) {
user.name = 'chalupajoe'
}
await user.save()

To learn how to update records, see the updating guide.

Destroying records

With records that are already in the database, deleting records is made trivial by Dream:

// in a single call
await user.destroy()

// in a bulk query
await User.where({ email: ops.ilike('%burpcollaborator%') }).destroy()

To learn how to delete records, see the destroying guide.

Associations

Take advantage of powerful association mechanisms within Dream to forge relationships between your models:

class Post extends ApplicationModel {
@deco.BelongsTo('User')
public user: User

@deco.HasMany('Comment')
public comments: Comment[]

@deco.HasMany('Comment', { through: 'comments', source: 'replies' })
public replies: Comment[]
}

To learn how to build associations, see the associations guide

Validations

Use validations to ensure the integrity of data entering your system, empowering Psychic to automatically raise clean validation errors to the client in contexts where these validation criterion fail:

class User extends ApplicationModel {
@deco.Validates('contains', '@')
@deco.Validates('length', { min: 4 })
public email: DreamColumn<User, 'email'>

@deco.Validates('numericality', { min: 1, max: 5 })
public numCoachingSessions: DreamColumn<User, 'numCoachingSessions'>
}

To learn how to leverage validations, see the validations guide

tip

Dream provides a rich set of features for interacting with your database. We have only begun to get into it here, so we encourage you to explore the model guides to learn more.

Basic model interactions

Advanced concepts