Overview
Loopar Framework follows a modern full-stack JavaScript architecture that combines server-side rendering (SSR) with Vite, an Express.js backend, and a monorepo structure. The framework provides a unified environment for building data-driven applications with real-time updates and efficient bundling.
Architecture Layers
Loopar’s architecture consists of three main layers:
Core Layer Framework core, document system, and ORM
Server Layer Express.js backend with routing and middleware
Client Layer React + Vite with SSR and hydration
Core Framework
The main Loopar class orchestrates the entire framework:
packages/loopar/core/loopar.js
export class Loopar extends Document {
constructor () {
super ( "Loopar" );
this . dateUtils = dateUtils ;
this . server = new Server ();
this . db = new SequelizeORM ();
}
async init ({ tenantId , appsBasePath }) {
this . tenantId = tenantId ;
this . tenantPath = this . makePath ( this . pathRoot , "sites" , tenantId );
this . pathCore = ` ${ process . cwd () } /packages/loopar`
this . appsBasePath = appsBasePath ;
this . auth = new Auth (
this . authTokenName ,
this . getUser . bind ( this ),
this . disabledUser . bind ( this )
);
await this . initialize ();
await this . server . initialize ();
}
async initialize () {
console . log ( `......Initializing Loopar.......` );
await this . buildGlobalEnvironment ();
await this . loadConfig ();
await this . db . initialize ();
await this . build ();
await this . buildIcons ();
await tailwinInit ( this . tenantId );
}
}
export const loopar = new Loopar ();
The loopar singleton instance is exported and used throughout the application as the main entry point to framework features.
Server Architecture
The server layer uses Express.js with Vite integration for development and production builds:
packages/loopar/core/server/server.js
export class Server extends Router {
server = server ;
url = null ;
isProduction = process . env . NODE_ENV == 'production' ;
async initialize () {
if ( this . isProduction ) {
server . use ( compression ());
server . use ( zstdMiddleware ({
root: 'dist/client' ,
priority: [ 'zst' , 'br' , 'gz' ],
}));
} else {
this . vite = await createViteServer ({
server: {
middlewareMode: true ,
hmr: {
protocol: 'ws' ,
port: parseInt ( process . env . PORT ) + 10000 ,
}
},
appType: 'custom'
});
server . use ( this . vite . middlewares );
}
await this . #exposePublicDirectories ();
server . use ( useragent ());
this . #initializeSession ();
this . route ();
this . #start ();
}
}
Development vs Production
Vite Dev Server : Integrated middleware mode for HMR (Hot Module Replacement)
Fast Refresh : Instant updates without full page reloads
Source Maps : Full debugging support with original source
Port Offset : HMR runs on PORT + 10000
Static Assets : Pre-built from dist/client
Compression : Multiple compression algorithms (zstd, brotli, gzip)
Optimized Bundles : Code splitting and tree shaking
CDN Ready : Static assets can be served from CDN
Client Architecture
The client uses React with SSR for optimal performance:
Server-Side Rendering
import { renderToString } from "react-dom/server" ;
import { StaticRouter } from "react-router" ;
export async function render ( url , __META__ , req , res ) {
const { Workspace , View } = await Loader ( __META__ , "server" );
global . __REQUIRE_COMPONENTS__ = [];
global . ENVIRONMENT = "server" ;
const context = {};
const HTML = renderToString (
< Main
location = { url }
__META__ = { {
... __META__ ,
components: { Workspace , View }
} }
req = { req }
res = { res }
/> ,
context
);
__META__ . __REQUIRE_COMPONENTS__ = global . __REQUIRE_COMPONENTS__ ;
return { HTML };
}
Client-Side Hydration
import ReactDOM from "react-dom/client" ;
import { BrowserRouter } from "react-router" ;
( async () => {
const __META_SCRIPT__ = document . getElementById ( '__loopar-meta-data__' );
const __META__ = JSON . parse ( __META_SCRIPT__ ?. textContent || "{}" );
const { Workspace , View } = await Loader ( __META__ , "client" );
ReactDOM . hydrateRoot (
document . getElementById ( "__[loopar-root]__" ),
< BrowserRouter >
< ErrorBoundary >
< App
__META__ = { {
... __META__ ,
components: { Workspace , View },
environment: "client"
} }
/>
</ ErrorBoundary >
</ BrowserRouter >
);
})();
The client hydrates the server-rendered HTML, so ensure data consistency between server and client renders to avoid hydration mismatches.
Request Flow
Here’s how a typical request flows through the system:
Request : HTTP request hits Express server
Middleware : Authentication, session, body parsing
Router : Parses URL and determines document/action
Controller : Executes business logic
Document : Interacts with database through ORM
SSR : Renders React components to HTML
Response : Sends HTML to client
Hydration : Client takes over React tree
Monorepo Structure
Loopar uses a monorepo structure with packages and apps:
workspace/
├── packages/
│ └── loopar/
│ ├── core/ # Core framework
│ │ ├── loopar.js # Main class
│ │ ├── document/ # Document system
│ │ ├── controller/ # Controllers
│ │ └── server/ # Server layer
│ ├── src/
│ │ └── components/ # React components
│ └── apps/
│ └── core/ # Core modules
├── app/ # Client entry points
│ ├── entry-server.jsx
│ ├── entry-client.jsx
│ └── Router.jsx
├── sites/ # Multi-tenant data
│ └── [tenantId]/
│ ├── config/
│ └── uploads/
└── apps/ # Custom applications
Package Organization
Contains the framework’s core functionality:
Document system and ORM
Server and routing
Authentication and sessions
File management
Modular applications that extend functionality:
Core system modules
Custom business logic
Installable/uninstallable
Multi-tenant data storage:
Per-tenant configuration
Isolated uploads and assets
Theme customization
Database Layer
Loopar uses Sequelize ORM for database abstraction:
// Accessing the database
const user = await loopar . db . query ( 'User' )
. where ({ name: user_id })
. orWhere ({ email: user_id })
. select ( 'name' , 'email' , 'password' , 'disabled' , 'profile_picture' )
. first ();
// Getting document data
const doc = await loopar . db . getDoc ( "System Settings" , null , [ "*" ], { isSingle: 1 });
// Listing records
const rows = await loopar . db . getList ( 'Module' , [ 'name' , 'icon' ], { in_sidebar: 1 });
Build System
The framework includes an automated build system that:
Builds References : Creates document type registry from database
Generates Icons : Pre-loads required Lucide icons for performance
Builds Modules : Creates navigation structure from modules
Compiles Assets : Processes Tailwind and other assets
packages/loopar/core/loopar/builder.js
async build () {
console . log ( '......Building Loopar.......' );
await this . makeDefaultFolders ();
await this . buildRefs ();
// Build module navigation
const groupList = await this . db . getList ( 'Module Group' , [ 'name' , 'description' ]);
// Write configuration
await fileManage . setConfigFile ( 'loopar.config' , data );
await this . loadConfig ( data );
}
Multi-Tenancy
Loopar supports multi-tenancy out of the box:
Tenant Isolation : Separate directories per tenant
Shared Codebase : All tenants run the same framework version
Custom Themes : Per-tenant CSS and branding
Data Separation : Database-level tenant isolation
Next Steps
Documents Learn about the Document system for data models
Controllers Understand how controllers handle requests
Routing Explore the routing system
Components Build UI with React components