ORM

Typetron’s ORM is an Active Record implementation used to easily interact with the database. You can define Entities for each table in the database and use that to query, modify and remove records. Entities are classes that extend the Entity class from @Typetron/Database

Before you get started with the ORM, make sure you configured the database.

Creating an Entity

Every entity class is a file inside the Entities directory:

import { Column, Entity, ID, PrimaryColumn } from '@Typetron/Database'

export class Article extends Entity {
    @PrimaryColumn()
    id: ID

    @Column()
    title: string

    @Column()
    content: string
}

NOTE Typetron will support auto-migrations that will help you create the database schema automatically from your entities and then export a migration once you are finished with your changes. This wil become handy when working on a development environment.

By default, Typetron uses the lowercase name of the entity as the table name, which is article in this case. You can change the table name using the @Options decorator:

NOTE Typetron will have a pluralization feature, so you won’t need to write the table at plural manually.

import { Column, Entity, ID, Options, PrimaryColumn } from '@Typetron/Database'

@Options({
    table: 'articles',
})
export class Article extends Entity {
    @PrimaryColumn()
    id: ID

    @Column()
    title: string

    @Column()
    content: string
}

Each property inside the entity that has the @Column decorator will map one to one with the table column in the database. By default, the entity will try to find the columns with the same name as it’s properties. For the Article entity above, Typetron will try to find table called articles with columns: id, title and content. You can change the name of the columns that will match from the property by passing it as the first argument for the @Column decorator:

export class Article extends Entity {
    // ...
    @Column('body')
    content: string
}

This will match the content property of the Article entity with the body column from the articles table.

Getting records

Each entity is like a more powerful version of the Query Builder that you can use to get the data from the database:

const articles = await Article.get() // SELECT * FROM `article`
/*
[
  {
    "id": 1,
    "title": "Article one",
    "content": "Content for article one"
  },
  {
    "id": 2,
    "title": "Article two",
    "content": "Content for article two"
  }
]
*/ 

Each object inside articles array is an instance of the Article entity

You can get specific columns from the database by passing and array of properties to the .get() method:

const articles = await Article.get(['id', 'title']) // SELECT `id`, `title` FROM `article`
/*
[
  {
    "id": 1,
    "title": "Article one"
  },
  {
    "id": 2,
    "title": "Article two"
  }
]
*/ 

Getting one record

If you want to get only one record from the database you may use .find or first methods from the entity that will return a single instance of the queried entity:

const article = await Article.where('title', 'Article one').first()
/*
{
    "id": 1,
    "title": "Article one",
    "content": "Content for article one"
}
*/

The find method retrieves one record queried by its primary key:

const article = await Article.find(1)
/*
{
    "id": 1,
    "title": "Article one",
    "content": "Content for article one"
}
*/

The find method is similar to:

const article = await Article.where('id', 1).first() // 'id' is the default primary key

You can override the primary key of an entity by overriding the getPrimaryKey() method:

export class Article extends Entity {

    getPrimaryKey() {
        return 'article_id'
    }
}

This will make the find method use the article_id column instead of the default id one. By default, The getPrimaryKey method returns the id property as the name of the primary key of an entity.

Creating records

The .create method will create a new entity and save it in the database:

const article = await Article.create({
    title: 'Fresh article',
    content: 'Fresh content'
})

If you just want to create an entity without saving is into the database, you can simply instantiate it using the data needed and save it later if needed:

const article = new Article({
    title: 'Fresh article',
    content: 'Fresh content'
})
// ...
await article.save()

Updating records

To update an entity simply set the needed properties to the required values, then, call the save method:

const article = await Article.find(1)
article.title = 'Updated title'
await article.save()

If you want to update more fields at a time, you can use the .fill method:

const article = await Article.find(1)
article.fill({
    title: 'Updated title',
    content: 'Updated content'
})
await article.save()

Deleting records

To delete a record you can call the .delete method on an entity:

const article = await Article.find(1)
await article.delete()

If you want to delete more records, you can filter the entities and then delete them:

await Article.where('title', 'Article title').delete()

Timestamps

Usually, you want your entities to have a date when they were created, and a date when they were updated. This can be done using the @CreatedAt and/or @UpdatedAt decorators for date columns in your entity:

import { Column, CreatedAt, Entity, ID, PrimaryColumn, UpdatedAt } from '@Typetron/Database'

export class Article extends Entity {
    @PrimaryColumn()
    id: ID

    @Column()
    title: string

    @Column()
    content: string

    @CreatedAt()
    createdAt: Date

    @UpdatedAt()
    updatedAt: Date
}

Relationships

One to One

A one-to-one relationship is a very simple relationship between two entities. We can define this relationship using the HasOne type on a property on our main entity, followed by its inverse using BelongsTo type on our child entity. Here is an example:

import { BelongsTo, Column, Entity, HasOne, ID, PrimaryColumn, Relation } from '@Typetron/Database'

export class User extends Entity {
    @PrimaryColumn()
    id: ID

    @Column()
    name: string

    @Relation(() => Laptop, 'user')
    laptop: HasOne<Laptop>
}

class Laptop extends Entity {
    @PrimaryColumn()
    id: ID

    @Column()
    name: string

    @Relation(() => User, 'laptop')
    user: BelongsTo<User>
}

We can get the value of this relationship by accessing the laptop property of a user:

const user = await User.find(1)
const laptop = await user.laptop.get()
One to Many

This relationship is used when a parent entity can have one or more child entities. For example, a car make can have one or more car models:

import { BelongsTo, Column, Entity, HasMany, Relation } from '@Typetron/Database'

export class Make extends Entity {
    @Column()
    name: string

    @Relation(() => Model, 'make')
    models: HasMany<Model>
}

export class Model extends Entity {
    @Column()
    name: string

    @Relation(() => Make, 'models')
    make: BelongsTo<Make>
}

We can get the value of this relationship by accessing the models property of a car make:

const make = await Make.find(1)
const models = await make.models.get()
Many to Many

This is the most complex relationship of them all because it also requires an additional database table. For example, a user can have many roles attached to it, but also, a role can be attached to many users. We can define the relationship like this:

import { BelongsToMany, Column, Entity, Relation } from '@Typetron/Database'

export class User extends Entity {
    @Column()
    name: string

    @Relation(() => Role, 'users')
    roles: BelongsToMany<Role>
}

export class Role extends Entity {
    @Column()
    name: string

    @Relation(() => User, 'roles')
    users: BelongsToMany<User>
}

We can get the roles of a user in the same manner as before:

const user = await User.find(1)
const roles = await user.roles.get()