Module Structure
Loopar follows a convention-based directory structure for organizing modules:Each module component follows kebab-case naming convention. For example, “Customer Order” becomes “customer-order”.
Creating Your First Module
import { loopar, fileManage, BaseDocument } from "loopar";
export default class Customer extends BaseDocument {
async validateEmail() {
const email = this.email;
if (!email || !email.includes('@')) {
loopar.throw({
message: "Please provide a valid email address"
});
}
}
async beforeSave() {
await this.validateEmail();
}
async afterSave() {
console.log(`Customer ${this.name} saved successfully`);
}
}
import { BaseController, loopar } from "loopar";
export default class CustomerController extends BaseController {
async actionSendWelcome() {
if (this.hasData()) {
const customer = await loopar.getDocument('Customer', this.name);
// Send welcome email logic here
return this.success(`Welcome email sent to ${customer.email}`);
}
}
}
import path from "pathe";
import { loopar, fileManage } from "loopar";
import BaseDocument from "../../loopar/core/document/base-document.js";
export default class Installer extends BaseDocument {
async install(reinstall = false) {
loopar.installingApp = this.app_name;
console.log("Installing App", this.app_name);
await this.installData(reinstall);
loopar.installingApp = null;
await loopar.setApp({[this.app_name]: true});
await loopar.build();
console.log(`App ${this.app_name} installed successfully!`);
return `App ${this.app_name} installed successfully!`;
}
async installData(reinstall = false) {
const moduleRoute = loopar.makePath('apps', this.app_name);
const appData = await fileManage.getConfigFile('installer', moduleRoute);
// Installation logic
for (const e of Object.keys(appData.documents)) {
const ent = appData.documents[e];
const [constructor, name] = e.split(':');
if (ent.root) {
const entityData = await fileManage.getConfigFile(name, ent.root);
const doc = await loopar.newDocument(constructor, entityData);
await doc.save({ validate: false });
}
}
return `App ${this.app_name} installed successfully!`;
}
}
Document Lifecycle Hooks
Loopar provides several lifecycle hooks you can override in your document classes:Working with Values
Access and manipulate document data using thevalues() method:
File Naming Conventions
Loopar uses utility functions to convert names between formats:Importing and Using Loopar Utilities
Loopar provides a comprehensive set of utilities:Next Steps
Now that you understand module structure, learn about:- Controllers Guide - Handle HTTP requests and actions
- Authentication - Secure your modules
- File Management - Handle file uploads