Models & Queries
Flowra models mewarisi base class Service yang membungkus Knex dan menyediakan helper untuk read, write, dan timestamps.
Base class Model
app/Config/Model.js memperluas Service dan menyediakan default untuk table name, primary key, dan allowed fields.
const Model = require('../../Config/Model');
class UsersModel extends Model {
constructor(connection = 'default') {
super(connection);
this.setTable('users');
this.setPrimaryKey('id');
this.setAllowedFields(['id', 'username', 'email', 'password']);
this.setTimestamps(true);
this.setSoftDelete(false);
}
async findAll() {
return this.read().select('*').whereNull('deletedAt');
}
}
module.exports = UsersModel;
Helper di Service
Service menyediakan:
read()danwrite()untuk membangun querysave()dansaveBulk()untuk insertupdate()dandelete()untuk mutasireadKnex()untuk memakai read replica bila tersedia
Query Builder (Knex)
Query Builder Flowra adalah query builder Knex yang diakses melalui read() dan write(). Artinya Anda bisa memakai seluruh API Knex sambil menjaga alias koneksi tetap konsisten.
Karena model Flowra sudah mengatur this.tableName, Anda tidak perlu memanggil .from('table') untuk model yang sama kecuali saat melakukan join atau query lintas tabel.
Query read
Gunakan read() untuk select dan reporting:
getAll({ search, limit = 25, offset = 0 } = {}) {
const query = this.read()
.select('id', 'name', 'email', 'createdAt')
.orderBy('createdAt', 'desc')
.limit(limit)
.offset(offset);
if (search) {
query.where((builder) => {
builder
.where('email', 'like', `%${search}%`)
.orWhere('name', 'like', `%${search}%`);
});
}
return query;
}
getSingle(id) {
return this.read()
.select('id', 'name', 'email', 'createdAt')
.where({ id })
.first();
}
Join dan agregasi
Knex mendukung join dan agregasi untuk kebutuhan read yang lebih kaya:
getUsersWithTeams() {
return this.read()
.select('users.id', 'users.name', 'teams.name as teamName')
.leftJoin('teams', 'teams.id', 'users.teamId')
.orderBy('users.name', 'asc');
}
Write dan transaksi
Gunakan write() saat butuh mutasi. Bungkus perubahan kompleks dalam transaksi:
async updateProfile(id, payload) {
const trx = await this.write().transaction();
try {
await this.write().transacting(trx).update(payload).where({ id });
await trx.commit();
return this.getSingle(id);
} catch (error) {
await trx.rollback();
throw error;
}
}
Karena Query Builder ini adalah Knex, Anda bisa memakai pola Knex yang sudah familiar tanpa keluar dari layer model Flowra.
Query classes
Query adalah class ringan untuk operasi read tertentu:
class SystemInfoQuery {
async execute() {
return {
environment: process.env.NODE_ENV || 'development',
nodeVersion: process.version,
uptime: process.uptime(),
};
}
}
module.exports = SystemInfoQuery;
Wiring models dan queries
Daftarkan models dan queries di module container:
const { asClass } = require('awilix');
const UsersModel = require('./Users.model');
module.exports = (scope) => {
scope.register({
models: {
user: asClass(UsersModel).singleton(),
},
});
};
Gunakan query classes untuk reporting dan services untuk business logic. Ini membuat kode tetap jelas dan mudah dites.