Skip to content

Model ​

A Model is the table-level API returned by Toshihiko#define(). It creates Queries and Yukari rows and extends EventEmitter2.

typescript
const User = database.define('users', schema);

Model is primarily a TypeScript type export. Applications normally obtain the runtime object from define().

typescript
class Model<
  Name extends string,
  Schema extends SchemaDefinition,
  AdapterInstance extends AdapterLike = Adapter,
> extends EventEmitter2

Metadata ​

PropertyTypeDescription
nameNameName passed to define()
parent, toshihikoToshihiko<AdapterInstance>Owning instance; toshihiko is a getter alias
originalSchemaSchemaDefinitions passed to define()
schemaCompiledSchema<Schema>Compiled fields
primaryKeysreadonly Field<Schema[number]>[]Fields marked primaryKey: true
autoIncrementField, aiField<Schema[number]> | nullAuto-increment field; ai is a compatibility alias
nameToColumnNameToColumnMap<Schema>Logical field name to storage column
columnToNameReadonly<Record<string, FieldName<RowFromSchema<Schema>>>>Storage column to logical field name
fieldNamesMapFieldNamesMap<Schema>Logical field name to compiled Field
fieldColumnsMapReadonly<Record<string, Field<Schema[number]>>>Storage column to compiled Field
cacheCache | nullEffective Model Cache
optionsModelOptionsOptions passed to define()

A Model without primary keys emits a log warning during construction.

build() ​

typescript
build<const Input extends BuildInput<Schema>>(
  fields: Input & Record<
    Exclude<keyof Input, keyof RowFromSchema<Schema>>,
    never
  >,
): BuiltYukari<Name, Schema, Input, AdapterInstance>

Creates a new Yukari without writing it. Input keys are restricted to schema fields.

typescript
const user = User.build({ name: 'Yukari' });

user.name; // string
user.id;   // number | undefined when generated by the database

Fields supplied by the caller and fields with defaults are known on the returned type. Other schema fields remain optional until the row is loaded or assigned.

Query entry points ​

Each method creates a fresh Query<Name, Schema, AdapterInstance>.

typescript
where(condition: QueryWhere<RowFromSchema<Schema>>): Query<Name, Schema, AdapterInstance>
field(fields: string | readonly FieldName<RowFromSchema<Schema>>[]): Query<Name, Schema, AdapterInstance>
fields(fields: string | readonly FieldName<RowFromSchema<Schema>>[]): Query<Name, Schema, AdapterInstance>
limit(limit: number | string | readonly (number | string)[]): Query<Name, Schema, AdapterInstance>
limit(offset: number | string, count: number | string): Query<Name, Schema, AdapterInstance>
index(indexName: string): Query<Name, Schema, AdapterInstance>
order(order: QueryOrder<RowFromSchema<Schema>>): Query<Name, Schema, AdapterInstance>
orderBy(order: QueryOrder<RowFromSchema<Schema>>): Query<Name, Schema, AdapterInstance>
conn(connection: AdapterConnection<AdapterInstance> | null): Query<Name, Schema, AdapterInstance>
typescript
const query = User
  .where({ id: { $gte: 10 } })
  .fields(['id', 'name'])
  .orderBy({ id: 'desc' })
  .limit(20);

Immediate operations ​

These methods create a fresh Query and execute it immediately.

SignatureReturn type
count()Promise<number>
update(data: Partial<RowFromSchema<Schema>>)Promise<AdapterUpdateByQueryResult<AdapterInstance>>
delete()Promise<AdapterDeleteByQueryResult<AdapterInstance>>
execute(...args: AdapterQueryExecuteArguments<AdapterInstance>)Promise<AdapterExecuteResult<AdapterInstance>>

Model.update() and Model.delete() have no condition. Use where() first unless the whole table is intentionally targeted.

find() ​

Model.find() has the same overloads as Query.find(). It creates a fresh Query before execution.

typescript
await User.find();
await User.find({ single: true });
await User.find(true);
await User.find({ noCache: true }, true);

findOne() ​

typescript
findOne(): Promise<QueriedYukari<Name, Schema, AdapterInstance> | null>
findOne(toJSON: false): Promise<QueriedYukari<Name, Schema, AdapterInstance> | null>
findOne(toJSON: true): Promise<QueryJsonRow<Schema> | null>

Creates a fresh Query and asks the Adapter for one row.

findById() ​

typescript
findById(id: FindByIdInput<Schema>): Promise<QueriedYukari<Name, Schema, AdapterInstance> | null>
findById(id: FindByIdInput<Schema>, toJSON: false): Promise<QueriedYukari<Name, Schema, AdapterInstance> | null>
findById(id: FindByIdInput<Schema>, toJSON: true): Promise<QueryJsonRow<Schema> | null>

With one primary key, id may be that field's value. Composite keys use an object containing the key fields.

typescript
await User.findById(42);
await Membership.findById({ userId: 42, groupId: 7 });

Transactions ​

The following methods exist only when the Adapter declares the corresponding transaction methods.

typescript
beginTransaction(): Promise<AdapterTransactionConnection<AdapterInstance>>
commit(connection: AdapterTransactionConnection<AdapterInstance>): Promise<AdapterCommitResult<AdapterInstance>>
rollback(connection: AdapterTransactionConnection<AdapterInstance>): Promise<AdapterRollbackResult<AdapterInstance>>

The connection type comes from the selected Adapter. See Transactions.

Name and primary-key helpers ​

convertColumnToName() ​

typescript
convertColumnToName(column: string): FieldName | undefined
convertColumnToName(columns: readonly string[]): readonly (FieldName | undefined)[]
convertColumnToName(row: DataRow): DataRow

Unknown columns are omitted from object input and become undefined for string or array input.

getPrimaryKeysName() and getPrimaryKeysColumn() ​

Each returns a string for one key, an array for multiple keys, or an empty array when the Model has no primary key.

Custom methods ​

Methods declared in define(..., { methods }) are copied onto the Model.

typescript
const User = database.define('users', schema, {
  methods: {
    async findByName(name: string) {
      return this.where({ name }).find();
    },
    async findActive() {
      return this.where({ active: true }).find();
    },
  },
});

Inside either method, this contains the Model and both custom methods. The returned User exposes User.findByName() and User.findActive() with their inferred parameter and return types.

TypePurpose
Model<Name, Schema, Adapter>Model instance contract
ModelOptionsRuntime Model options
ModelDefinitionOptionsOptions accepted by define()
BuildInput<Schema>Schema-restricted build() input
BuiltRowFromSchema<Schema, Input>Known and optional fields after build()
InferModelRow<typeof Model>Full row value type
InferModelPrimaryKey<typeof Model>Union of primary-key field names

Released under the MIT License.