Frappe Framework Architecture (Version 15)
Introduction – What Is the Architecture of Frappe Framework?
The Frappe Framework is a full-stack, metadata-driven, Python-based application framework.
Its architecture powers ERPNext and countless enterprise-grade applications.
In Version 15, Frappe maintains a clean, modular design built on:
- Metadata
- MVC architecture
- Python server
- JavaScript frontend
- Jinja-based website engine
- Real-time components
- REST API layer
This guide explains each part of the architecture, how they work together, and why Frappe is uniquely suited to build business applications.
What Is the Core Philosophy Behind Frappe?
Frappe’s design philosophy is based on:
1. Metadata-driven design
Everything—UI, data structure, permissions, workflows—is defined in metadata (JSON).
Frappe automatically generates:
- Database tables
- Form UI
- List view
- REST API
- Controllers
2. Convention over configuration
- Minimal boilerplate.
- DocType defines almost everything.
3. Server + Client unified
Python backend and JS frontend work seamlessly with shared metadata.
4. Modular app ecosystem
Each app is plug-and-play.
High-Level Architecture Overview
Frappe’s architecture can be divided into key layers:
- Database Layer (MariaDB)
- Model Layer (DocTypes)
- Controller Layer (Python + JS)
- View Layer (Desk UI + Website)
- Business Logic Layer
- REST API Layer
- Real-Time Engine
- Background Job System
- Bench Toolchain
Each component works together to deliver a complete enterprise framework.
1. Database Layer — MariaDB
Frappe uses MariaDB as its primary transactional database.
Every DocType becomes a database table:
tabCustomer
tabSales Invoice
tabEmployee
Special Behavior:
- All DocTypes share a common set of default fields (owner, creation, modified).
- Child tables are stored separately and linked via parent + parenttype.
2. Model Layer — DocTypes (Metadata + ORM)
DocTypes represent:
- Database Schema
- Data Model
- Permissions
- Fields
- Layout
- Business Rules
Metadata is stored in JSON (doctype.json).
At runtime, Frappe loads this metadata to:
- Generate UI
- Validate documents
- Build API
- Enforce rules
- The Document class acts as Frappe’s ORM for create/read/update/delete operations.
Example:
doc = frappe.get_doc("Customer", "CUST-0001")
doc.customer_name = "New Name"
doc.save()
3. Controller Layer — Python & JavaScript Events
Python Controllers
Each DocType has a backend controller (doctype.py) extending Document.
Common Hooks:
- validate
- before_save
- after_insert
- on_submit
- on_cancel
JavaScript Controllers
located in doctype.js
Used for client-side events:
- field triggers
- form refresh
- dynamic UI changes
4. View Layer — Desk + Web
Desk (Admin UI)
Single-page app built using JavaScript.
Provides:
- Form view
- List view
- Kanban
- Calendar
- Gantt
- Dashboard
- Report builder
All views are auto-generated using metadata.
Website Engine
The website framework uses:
- Jinja templates
- Dynamic routing
- Portal pages
- Web forms
- Guest vs logged-in context
Frappe powers both the ERP backend and public-facing websites.
5. Business Logic Layer
Frappe allows multiple options to implement business logic:
- Controller methods
- Server Scripts
- Client Scripts
- Hooks
- Workflows
- Automation rules
This keeps the system clean and modular.
6. REST API Layer – Automatic API Generation
Every DocType automatically exposes:
| Method | Endpoint |
| GET | /api/resource/<doctype> |
| POST | /api/resource/<doctype> |
| PUT | /api/resource/<doctype>/<name> |
| DELETE | /api/resource/<doctype>/<name> |
Developers can also create custom endpoints using:
@frappe.whitelist()
def custom_api():
return {"status": "ok"}
API Authentication:
- OAuth2
- API Key & Secret
- Token-based
- Session
7. Real-Time Engine — WebSockets (Socket.IO)
Frappe includes built-in real-time features:
- Notifications
- Live updates
- Chat
- Background job status
- Real-time dashboards
Implemented using Socket.IO + Redis.
Example:
frappe.realtime.on("msgprint", data => console.log(data));
8. Background Job System (Queue Workers)
Frappe uses Redis Queue (RQ) for async tasks.
Example:
frappe.enqueue(“my_app.tasks.send_email”)
Workers execute tasks separately from the main request lifecycle.
9. Bench — The Developer Toolchain
Bench is used for:
- Site management
- App installation
- Database migrations
- Background worker setup
- NGINX & Supervisor setup
- Backup and restore
- Asset building
Example commands:
bench new-site site1.local
bench start
bench migrate
The Frappe Request Lifecycle (v15)
- Request hits NGINX
- Forwarded to Gunicorn
- Gunicorn hands it to Frappe App Server
- Authentication & routing
- Controller execution
- DocType logic executed
- Response rendered
- Logs & background jobs queued
The lifecycle integrates seamlessly across layers.
Directory Structure of a Frappe App (v15)
my_app/
├── my_app/
│ ├── modules.txt
│ ├── config/
│ ├── public/
│ ├── templates/
│ ├── hooks.py
│ └── doctype/
│ └── item/
│ ├── item.json
│ ├── item.py
│ ├── item.js
│ └── item_list.js
└── MANIFEST.in
Each file has a clear architectural purpose.
Why This Architecture Works Well
| Strength | Benefit |
| Metadata-driven | Build apps fast with less code |
| Unified backend + frontend | Consistent, stable development |
| Modular design | Plug-and-play apps |
| Integrated permissions | Secure by design |
| Auto-generated UIs | Faster user-facing features |
| REST API out of the box | Easy integrations |