Documentation Index
Fetch the complete documentation index at: https://docs-kfhye.zochil.dev/llms.txt
Use this file to discover all available pages before exploring further.
Naming Conventions
Files and Directories
- Routes: Use kebab-case for route files:
user-profiles/routes.ts
- Services: Use PascalCase for service files:
UserProfileService.ts
- Directories: Use kebab-case:
user-api/, merchant-profiles/
- API Endpoints: Use kebab-case in URLs:
/user-profiles/, /merchant-settings/
Code Elements
- Classes: PascalCase -
UserService, ProductCatalogService
- Methods: camelCase -
getUserProfile(), createProduct()
- Variables: camelCase -
userId, productData, isAuthenticated
- Constants: UPPER_SNAKE_CASE -
DATABASE_URL, JWT_SECRET
- Interfaces/Types: PascalCase -
DBConnection, ApiRequest
Database Schema
- Tables: snake_case -
user_profiles, product_categories
- Columns: snake_case -
user_id, created_at, merchant_id
- Multi-tenancy: Always include
shop_id or merchant_id
Import Organization
Organize imports in the following order with blank lines between groups:
// 1. Standard library imports
import { Router } from "express";
import { body as checkBody } from "express-validator";
// 2. Core modules (using 'core' alias)
import APIService from "core/base/service";
import { DBConnection, ID } from "core/types";
import { authenticateUser } from "core/auth/middlewares";
// 3. Shared libraries (using 'lib' alias)
import { CustomError } from "lib/errors/custom_error";
import kv from "lib/connectors/redis";
// 4. Local modules (relative imports)
import SomeService from "./service";
import { validateInput } from "../utils/validation";
Module Structure
Service Class Pattern
All service classes must extend APIService and follow this pattern:
import APIService from "core/base/service";
import { DBConnection, ID } from "core/types";
export default class ModuleService extends APIService {
constructor(db: DBConnection) {
super(db, "table_name");
}
async list(filters = {}, pagination = {}) {
return await this.findForList(filters, pagination);
}
async detail(id: ID) {
return await this.findOneOrThrow(id);
}
async create(data: any) {
// Validation logic
return await this.create(data);
}
}
Route Handler Pattern
Route files must export a function that accepts DBConnection:
import { Router } from "express";
import { body as checkBody } from "express-validator";
import ModuleService from "./service";
import { DBConnection } from "core/types";
export default (db: DBConnection) => {
const routes: any = Router();
const service = new ModuleService(db);
routes.get(
"/list",
service.handleOk(
async (req) =>
await service.list(req.query, {
page: parseInt(req.query.page as string) || 1,
limit: parseInt(req.query.limit as string) || 50
})
)
);
return routes;
};
Error Handling
Custom Error Usage
Always use CustomError from the shared library:
import { CustomError } from "lib/errors/custom_error";
// In service methods
if (!user) {
throw new CustomError("USER_NOT_FOUND", "User not found with given ID", {
userId: id
});
}
if (!req.currentUser.is_super) {
throw new CustomError("FORBIDDEN", "Super admin access required");
}
Response Handling
Use the service.handleOk() pattern for consistent responses:
routes.post(
"/create",
[checkBody("name").notEmpty()],
service.handleOk(async (req) => {
const result = await service.create(req.body);
return { message: "Created successfully", data: result };
})
);
Database Access
Multi-tenancy Pattern
Always filter by merchant/shop ID for data isolation:
async getUsersByShop(shopId: ID) {
return await this.connector.db("users")
.where("shop_id", shopId)
.select("*");
}
async createProduct(data: any, merchantId: ID) {
return await this.connector.db("products")
.insert({
...data,
shop_id: merchantId,
created_at: new Date()
});
}
Query Patterns
Use Knex.js patterns consistently:
// List with pagination
async findForList(filters = {}, pagination = { page: 1, limit: 50 }) {
const query = this.connector.db(this.tableName);
// Apply filters
if (filters.status) {
query.where("status", filters.status);
}
// Apply pagination
const offset = (pagination.page - 1) * pagination.limit;
query.offset(offset).limit(pagination.limit);
return await query;
}
Authentication & Authorization
Request Context Usage
Access user context from authenticated requests:
routes.get(
"/profile",
service.handleOk(async (req) => {
// User data available after authentication
const userId = req.currentUser.id;
const merchantId = req.merchant_id;
return await service.getUserProfile(userId, merchantId);
})
);
Role-based Access
Check permissions using user context:
// Check super admin status
if (!req.currentUser.is_super) {
throw new CustomError("FORBIDDEN", "Super admin required");
}
// Check specific roles
const hasPermission = req.currentUser.roles.includes("merchant_manager");
if (!hasPermission) {
throw new CustomError("FORBIDDEN", "Insufficient permissions");
}
Event Publishing
Domain Events
Publish events for important domain actions:
async createOrder(orderData: any) {
const order = await this.create(orderData);
// Publish domain event
await this.publishEvent("order.created", {
orderId: order.id,
merchantId: order.merchant_id,
amount: order.total,
timestamp: new Date()
});
return order;
}
TypeScript Usage
Type Safety
Use proper typing throughout:
import { ID, ApiRequest } from "core/types";
interface CreateProductRequest {
name: string;
price: number;
category_id: ID;
merchant_id: ID;
}
async createProduct(data: CreateProductRequest): Promise<Product> {
return await this.create(data);
}
Path Aliases
Use configured path aliases for imports:
// Use 'core' alias for @core modules
import APIService from "core/base/service";
import { DBConnection } from "core/types";
// Use 'lib' alias for lib modules
import { CustomError } from "lib/errors/custom_error";
import kv from "lib/connectors/redis";