# Defining models
Models are the central concept in trilogy — they're the containers
that all objects are created in and conform to, and they map to SQLite's
tables. You create them with a schema that defines what properties each
object will contain and what type each of those fields are. In SQLite
you would call these objects rows and its properties columns, where
a column has a type such as TEXT
or INTEGER
. In trilogy, those types
are defined using JavaScript's usual suspects: String
, Number
, and
even Array
, Object
, and more.
Let's first get trilogy imported and a database created:
import { connect } from 'trilogy'
const db = connect('./storage.db')
Now, let's create a users
model:
const userSchema = {
name: String,
age: Number,
birthdate: Date,
id: 'increments'
}
const users = await db.model('users', userSchema)
There is now a users
model (table) in our SQLite database, with the
properties (columns) name
, age
, birthdate
, and id
. The first three
are pretty obvious data types, while the third uses a special 'increments'
type, which is short for the equivalent super verbose SQL
integer not null primary key autoincrement
.
# Property types
type | description |
---|---|
String | stored as text |
Number | stored as integer |
Boolean | stored as integer |
Date | stored as text (ISO formatted string (opens new window)) |
Array | stored as text using JSON.stringify , returned using JSON.parse |
Object | stored as text using JSON.stringify , returned using JSON.parse |
'json' | stored as text using JSON.stringify , returned using JSON.parse |
'increments' | set as an auto-incrementing integer & primary key |
# Property attributes
There are also various attributes you can apply to a property. In that case we move away from what is actually a shorthand for the property definition to its equivalent object definition where we can then add more attributes:
const userSchema = {
- name: String,
+ name: { type: String, primary: true }
age: Number,
birthdate: Date,
id: 'increments'
}
attribute | type | description |
---|---|---|
primary | boolean | Whether to set this property as the primary key. |
defaultTo | any | Default value to use when absent. |
unique | boolean | Whether the property is required to be unique. |
nullable | boolean | Whether to allow null values. |
notNullable | boolean | Works inversely to nullable . |
index | string | Specifies the property as an index with the provided name. |
get | Function | Triggered on selects, receives the raw value and should return a new value. |
set | Function | Triggered on inserts, receives the input value and should return a new value. |
const myGamesSchema = {
name: { type: String, primary: true },
category: { type: String, defaultTo: 'backlog' }
genre: String,
owned: Boolean
}
# Property descriptors
Now that you've seen types and attributes, the values collectively known
as a property's descriptor, let's move on to take a deeper dive into the
property definitions of a schema for a cars
model.
db.model('cars', {
id: 'increments',
make: { type: String, defaultTo: 'Ford' },
model: { type: String, nullable: false },
year: Number
})
The schema is the object passed as the second argument to model()
.
Each key of this object is the name of a property, so in the model
cars
, there are 4 properties: id
, make
, model
, and year
.
Let's break down each descriptor, starting with id
:
{
id: 'increments',
make: { type: String, defaultTo: 'Ford' },
model: { type: String, nullable: false },
year: Number
}
The id
property is defined with 'increments'
as its type. As shown
earlier in the types section, this is a special type that's really a shortcut
for the super long SQL integer not null primary key autoincrement
. It
declares id
as a field that will automatically set itself to the last
inserted row's id + 1, and is the primary key of the table — the one
that prevents duplicates.
Remember that you can define other types and attributes by providing an object
instead of just the type. This is done with the next descriptor, make
:
{
id: 'increments',
make: { type: String, defaultTo: 'Ford' },
model: { type: String, nullable: false },
year: Number
}
Here, we don't use a string value to declare the type. We use the standard
JavaScript String
constructor. You can do the same with Number
, Boolean
,
and Date
as seen eariler. This stores make
as a text
column in SQLite.
We also use the defaultTo
property to set a value that should be used
when make
isn't provided at creation time.
Next up is model
(not to be confused with trilogy's model
):
{
id: 'increments',
make: { type: String, defaultTo: 'Ford' },
model: { type: String, nullable: false },
year: Number
}
model
is also a String
type, but in this case we set the nullable
property to false. This essentially means model
is a required property.
And finally year
:
{
id: 'increments',
make: { type: String, defaultTo: 'Ford' },
model: { type: String, nullable: false },
year: Number
}
Back to basics on this one. It's defined with the same shorthand as id
,
only this time it's a Number
. This is stored as an integer
column in
SQLite.