Build enterprise applications with declarative JSON schemas instead of thousands of lines of boilerplate. Nor Abon generates your database, API and administration interface, leaving only your business logic to implement.
Nor Abon brings together declarative JSON schemas and custom business logic in a unified AI-native architecture that automatically generates APIs and admin panels.
JSON database schemas
Describe entities, fields, relationships, and data storage structures.
JSON objects schemas
Define application entities, permissions, and field behavior logic.
JSON commands schemas
Define the list of available commands for entities and configure permissions.
JSON pages schemas
Describe administrative pages, forms, tables, and other interface elements.
Custom logic
Extend standard functionality with isolated business logic.
Nor Abon provides a universal API layer for a SaaS ecosystem.
It enables any external or internal clients (web, mobile, desktop, IoT, and automated services) to connect to a unified core of business logic.
Most of the application is defined using compact JSON schemas instead of large files with mixed logic.
Every Nor Abon project follows the same architectural principles. Pages, components, objects, actions, and APIs are defined consistently, eliminating dozens of competing patterns and conventions.
Most repetitive infrastructure code is generated automatically by the framework. There's no need to manually create controllers, DTOs, services, forms, access policies, or other boilerplate components.
Less boilerplate means fewer bugs, easier maintenance, and faster development.
Nor Abon automates the repetitive parts of CRM and SaaS development through declarative schemas. Define your data model once, and let the framework build the database, API, validation and administrative interface.
{
"title": "Add account",
"type": "add",
"object_scheme": "accounts",
"required_permissions": [ "accounts", "accounts_add" ]
}
{
"title": "Clients",
"table": "clients",
"is_trash": true,
"properties": [
{
"title": "First name",
"article": "first_name",
"data_type": "string",
"field_type": "string",
"is_default_in_list": true,
"is_unique": false,
"is_autofill": true,
"required_permissions": [],
"use_in_actions": ["add", "update"],
"require_in_actions": ["add"]
},
{
"title": "Last name",
"article": "last_name",
"data_type": "string",
"field_type": "string",
"is_default_in_list": true,
"is_unique": false,
"is_autofill": true,
"required_permissions": [],
"use_in_actions": ["add", "update"],
"require_in_actions": []
},
{
"title": "Patronymic",
"article": "patronymic",
"data_type": "string",
"field_type": "string",
"is_default_in_list": false,
"is_unique": false,
"is_autofill": true,
"required_permissions": [],
"use_in_actions": ["add", "update"],
"require_in_actions": []
},
{
"title": "Email",
"article": "email",
"data_type": "email",
"field_type": "email",
"is_default_in_list": false,
"is_unique": true,
"is_autofill": true,
"required_permissions": [],
"use_in_actions": ["add", "update"],
"require_in_actions": []
},
{
"title": "Phone",
"article": "phone",
"data_type": "phone",
"field_type": "phone",
"is_default_in_list": false,
"is_unique": true,
"is_autofill": true,
"required_permissions": [],
"use_in_actions": ["add", "update"],
"require_in_actions": []
}
]
}
{
"title": "Call history",
"properties": [
{
"title": "Employee ID",
"article": "employee_id",
"type": "int",
"default": null,
"is_required": "Y"
},
{
"title": "Client ID",
"article": "client_id",
"type": "int",
"default": null,
"is_required": "N"
},
{
"title": "Duration (sec)",
"article": "duration",
"type": "int",
"default": null,
"is_required": "N"
},
{
"title": "Record",
"article": "record",
"type": "varchar(255)",
"default": null,
"is_required": "N"
},
{
"title": "Created at",
"article": "created_at",
"type": "datetime",
"default": "CURRENT_TIMESTAMP",
"is_required": "Y"
}
]
}
{
"required_permissions": [],
"required_modules": [],
"structure": {
"header": {
"title": "Page header",
"size": 3,
"type": "header",
"required_permissions": [],
"required_modules": [],
"settings": {
"title": "users",
"annotation": "",
"breadcrumbs": []
},
"components": {}
},
"users": {
"title": "Users list",
"type": "list",
"size": 4,
"settings": {
"object": "users"
},
"components": {
"filters": [
{
"title": "Role",
"type": "list",
"settings": {
"is_search": false,
"recipient_property": "role_id",
"donor_object": "roles",
"donor_property_title": "title",
"donor_property_value": "id"
},
"required_permissions": []
}
]
}
}
}
}
if ( !$requestData->sort_by ) {
$requestData->sort_by = "last_name";
}
if ( $requestData->context->block === "list" ) {
/**
* Specifying the Promotion Period and Type
*/
$returnRows = [];
foreach ( $response[ "data" ] as $row ) {
$detailPromotion = $API->DB->from( "promotions" )
->where( "id", $row[ "id" ] )
->limit( 1 )
->fetch();
$row[ "period" ] = date( 'Y-m-d H:i', strtotime( $row[ "start" ] ) ) .
" - " . date( 'Y-m-d H:i', strtotime( $row[ "end" ] ) );
if ( $detailPromotion[ "promotion_type" ] == "percent" )
$row[ "value" ] = $row[ "value" ] . "%";
else if ( $row[ "promotion_type" ] == "fixed" )
$row[ "value" ] = $row[ "value" ] . "$";
$returnRows[] = $row;
} // foreach. $response[ "data" ]
$response[ "data" ] = $returnRows;
} // if. $requestData->context->block === "list"
$listOfStores = $API->DB->from( "stores" )
->where( "is_active", 'Y' );
foreach ( $listOfStores as $store ) {
$payments = mysqli_query(
$API->DB_connection,
"SELECT * FROM sales WHERE pay_type = \"sell\" AND is_active = \"Y\" AND store_id = {$store[ "id" ]}"
);
$incomeBalance = $API->DB->from( "cashboxBalance" )
->where( "store_id", $store[ "id" ] )
->orderBy( "created_at DESC" )
->limit( 1 )
->fetch();
$paymentsSummary = 0;
$incomeBalance = $incomeBalance[ "balance" ] ?? 0;
foreach ( $payments as $payment ) $paymentsSummary += $payment[ "summary" ];
$paymentsSummary += $incomeBalance;
$API->DB->insertInto( "cashboxBalance" )
->values([
"balance" => $paymentsSummary,
"store_id" => $store[ "id" ]
])
->execute();
} // foreach. $listOfStores