✨ Basics
Models

The Models

Models in AdonisJS

Models in AdonisJS represent the data structure of your application and allow you to interact with the database. They define the properties, relationships, and behaviors of your data entities.

Creating a Model

There are two ways to create a model in AdonisJS.

1. Using the command line

You can create a model using the command line.

node ace make:model User

This will create a new file in the app/models folder named User.ts.

2. Create a model manually

You can create a model manually by creating a new file in the app/models folder.

app/account/models/user.ts
import { DateTime } from "luxon";
import { withAuthFinder } from "@adonisjs/auth";
import hash from "@adonisjs/core/services/hash";
import { compose } from "@adonisjs/core/helpers";
import { BaseModel, beforeCreate, beforeSave, column, hasMany, hasOne, manyToMany } from "@adonisjs/lucid/orm";
import * as relations from "@adonisjs/lucid/types/relations";
import { v4 as uuidv4 } from "uuid";
import Address from "./address.js";
import Otp from "#auth/models/otp";
import Payment from "#common/models/payment";
import Product from "#common/models/product";
 
const AuthFinder = withAuthFinder(() => hash.use("scrypt"), {
  uids: ["email"],
  passwordColumnName: "password"
});
 
export default class User extends compose(BaseModel, AuthFinder) {
  @column({ isPrimary: true })
  declare id: string;
 
  @column()
  declare addressId: number;
 
  @column()
  declare name: string;
 
  @column()
  declare surname: string;
 
  @column()
  declare email: string;
 
  @column({ serializeAs: null })
  declare password: string | null;
 
  @column()
  declare role: string;
 
  @column()
  declare rememberMeToken: string | null;
 
  @column()
  declare provider: string;
 
  @hasOne(() => Address)
  declare address: relations.HasOne<typeof Address>;
 
  @hasMany(() => Otp)
  declare otps: relations.HasMany<typeof Otp>;
 
  @manyToMany(() => Product, {
    pivotColumns: [
      "subscription_period",
      "subscription_start_date",
      "subscription_end_date",
      "checkout_session_id",
      "subscription_id",
      "subscription_status"
    ]
  })
  declare products: relations.ManyToMany<typeof Product>;
 
  @hasMany(() => Payment)
  declare payments: relations.HasMany<typeof Payment>;
 
  @column.dateTime({ autoCreate: true })
  declare createdAt: DateTime;
 
  @column.dateTime({ autoCreate: true, autoUpdate: true })
  declare updatedAt: DateTime;
 
  @beforeCreate()
  static assignUserId(user: User) {
    user.id = uuidv4();
  }
 
  @beforeSave()
  static async updateUpdatedAt(user: User) {
    user.updatedAt = DateTime.now();
  }
 
  static isInformationComplete(user: User) {
    return user.name !== null || user.surname !== null;
  }
}

Model Properties

  • Columns: Define the fields in your model, each representing a column in the corresponding database table. For example, name, surname, and email are columns in the User model.
  • Primary Key: The id field is marked as the primary key using { isPrimary: true }.

Lifecycle Hooks

Lifecycle hooks allow you to execute code at specific points in a model's lifecycle.

  • @beforeCreate(): Executes code before a new record is inserted into the database.
  • @beforeSave(): Executes code before a record is updated in the database.

Example Hooks

  • assignUserId: Assigns a unique UUID to the id field before creating a new User.
  • updateUpdatedAt: Updates the updatedAt field with the current date and time before saving a User.

Relationships

Models can define relationships with other models to represent associations between different entities.

Has One

A one-to-one relationship. For example, a User has one Address.

app/account/models/user.ts
@hasOne(() => Address)
declare address: relations.HasOne<typeof Address>;
app/account/models/address.ts
  @column()
  declare userId: string;
 
  @belongsTo(() => User)
  declare user: relations.BelongsTo<typeof User>;

The address belongs to the user, so we need to define a relationship between the two models.

Has Many

A one-to-many relationship. For example, a User can have many Otp and Payment entries.

app/account/models/user.ts
@hasMany(() => Otp)
declare otps: relations.HasMany<typeof Otp>;
 
@hasMany(() => Payment)
declare payments: relations.HasMany<typeof Payment>;
app/auth/models/otp.ts
  @column()
  declare userId: string
 
  @belongsTo(() => User)
  declare user: relations.BelongsTo<typeof User>

One more time, the otp belongs to the user, so we need to define a relationship between the two models.

Many to Many

A many-to-many relationship. For example, a User can have many Products.

@manyToMany(() => Product, {
  pivotColumns: [
    "subscription_period",
    "subscription_start_date",
    "subscription_end_date",
    "checkout_session_id",
    "subscription_id",
    "subscription_status"
  ]
})
declare products: relations.ManyToMany<typeof Product>;

You don't need to define a relationship between the User and Product models, because they are already related. You also don't need to define the pivot model, it will be automatically created. You'll need to define the pivot columns, if needed, and the migration table too. Please refer to the AdonisJS ORM documentation (opens in a new tab) for more information. Or you can check our documentation for more examples.

Authentication

The User model uses the withAuthFinder mixin to handle authentication.

const AuthFinder = withAuthFinder(() => hash.use("scrypt"), {
  uids: ["email"],
  passwordColumnName: "password"
});
 
export default class User extends compose(BaseModel, AuthFinder) {
  ...
}

See more

By understanding how models work, you can effectively manage the data structure and relationships in your AdonisJS application, ensuring a well-organized and efficient database interaction.