DEV Community

Sai Praveen Sanapalli
Sai Praveen Sanapalli

Posted on

Getting Started with Frappe Framework: From Basics to Architecture & Internals

If you've ever wondered how ERPNext, HRMS, Helpdesk, and dozens of other production-grade business apps got built so fast — the answer is Frappe. It's a metadata-driven, low-code and full-code Python framework that handles the boring-but-critical 80% (auth, permissions, forms, REST APIs, workflows, reports) so you can focus on actual business logic.

While learning Frappe, and turned them into this single guide. It's split into three parts:

  1. Core Concepts — what Frappe is, how to set it up, and how apps/sites are structured
  2. Development Essentials — the day-to-day toolkit: controllers, hooks, ORM, scripts, reports, APIs
  3. Framework Internals — what actually happens under the hood, from request to response

Let's dive in.


Part 1: Core Concepts

What is Frappe?

Frappe is an open-source, metadata-driven, low-code/full-code Python framework for building modern business applications. It bundles database management, forms, auth, role-based permissions, workflows, automation, reports, and REST APIs out of the box — so you're not reinventing the wheel for every project.

It's the engine behind ERP, CRM, HRMS, Helpdesk, Manufacturing, Healthcare, Education, and many other custom business solutions.

Prerequisites

Before you install Bench (Frappe's CLI), make sure you have:

Requirement Version
OS Ubuntu 22.04 LTS+ / macOS / Linux
Python 3.11+
Node.js 20+
Yarn 1.22.x
Database PostgreSQL 14+ / MariaDB 10.6+
Redis 6+
Git latest
wkhtmltopdf for PDF generation
Bench CLI official CLI tool

Installing Bench

Bench is the official CLI tool used to set up and manage Frappe instances (technically called benches):

pip3 install frappe-bench
bench init my-bench
cd my-bench
Enter fullscreen mode Exit fullscreen mode

The Bench Directory Structure

my-bench/
├── apps/            # Installed apps
├── sites/           # Sites (databases)
├── config/          # Config files
├── logs/            # Logs
├── sites/assets/     # Public assets
└── Procfile          # Process configuration
Enter fullscreen mode Exit fullscreen mode

Creating an App

bench new-app my_app
Enter fullscreen mode Exit fullscreen mode

This scaffolds a full app structure:

my_app/
├── my_app/
│   ├── doctype/      # Custom DocTypes
│   ├── report/       # Report scripts
│   ├── page/         # Custom pages
│   ├── api/          # API endpoints
│   ├── utils/        # Utility functions
│   ├── www/          # Public web assets (CSS, JS, images)
│   ├── hooks.py       # App hooks & overrides
│   ├── patches.txt    # Patches for migrations
│   └── modules.txt    # List of modules in the app
├── tests/             # Unit & integration tests
├── README.md
├── requirements.txt
└── setup.py
Enter fullscreen mode Exit fullscreen mode

Creating a Site

bench new-site site.local
Enter fullscreen mode Exit fullscreen mode

Every site gets its own isolated structure:

sites/site.local/
├── site_config.json   # DB, email, redis config
├── public/             # Public files (assets, uploads)
├── private/            # Private files (backups, keys)
└── logs/               # Site-specific logs
Enter fullscreen mode Exit fullscreen mode

The key idea: each site has its own database, configuration, and files — this is what enables Frappe's multi-tenancy.

Installing an App into a Site

bench --site site.local install-app my_app
bench --site site.local migrate
Enter fullscreen mode Exit fullscreen mode

This installs the app in the site and creates the necessary tables, assets, roles, and permissions.

Modules & DocTypes

A module groups related functionality within an app (e.g., Accounts, HR, Selling, Buying).

A DocType is the fundamental building block of Frappe — it defines the structure and behavior of data, and is rendered automatically into forms, lists, and reports.

Types of DocTypes:

  • Regular DocType — a standard doctype with its own database table
  • Single — a doctype with only one record (e.g., global settings)
  • Child Table — a table nested inside a parent DocType
  • Submittable DocType — supports workflow states (submit/cancel)
  • Virtual DocType — not stored in the DB; used for calculated/reporting data
  • Custom DocType — created by developers for custom requirements

Common Bench Commands

Command Purpose
bench start Start all services
bench stop Stop all services
bench restart Restart all services
bench new-site <site> Create a new site
bench get-app <app> Download an app
bench install-app <app> Install app in a site
bench migrate Run database migrations
bench update Update apps
bench --site <site> console Open the site console
bench --site <site> backup Backup a site
bench list-apps List installed apps
bench list-sites List all sites

Notable Frappe Features

Authentication (Login, OAuth, LDAP, 2FA, Social Login), granular role-based permissions, dynamic form & list views, a REST API out of the box, workflow & automation engine, integrated Email/SMS/WhatsApp, background jobs & scheduler, built-in reports & dashboards, secure file management, multi-tenancy, and full extensibility — all under the MIT license.


Part 2: Development Essentials

Once your app and site are set up, here's the toolkit you'll actually use day-to-day.

1. Controllers & Methods

DocTypes ship with a Python controller class where you hook into the document lifecycle:

def before_insert(self): ...
def before_save(self): ...
def before_submit(self): ...
def on_update(self): ...
def on_submit(self): ...
def on_cancel(self): ...
def after_insert(self): ...
def on_trash(self): ...
Enter fullscreen mode Exit fullscreen mode

These give you precise control over what happens at every stage of a document's life.

2. Users, Roles & Permissions

Frappe's permission system is deep:
Users & Roles,
Role Permission Manager,
Permission Levels (0–3),
User Permissions,
Document Permissions,
Permission Query Conditions,
Field-Level Permissions,
Share & Assign,
conditional (if-based) rules,
User/Role Profiles.

3. Hooks

hooks.py is where an app declares itself to the framework. Key hooks include:

  • doc_events → document event handlers
  • scheduler_events → background scheduled jobs
  • override_whitelisted_methods → override REST API methods
  • override_doctype_class → replace a DocType's controller
  • doctype_js → customize form behavior
  • app_include_js/css & web_include_js/css → load Desk/Website assets
  • jinja → custom template methods
  • fixtures → export app customizations
  • permission_query_conditions → custom list permissions
  • on_login / on_logout → authentication hooks

4. Database, ORM & Query

Frappe gives you multiple layers to work with data:

  • Frappe ORM (frappe.db, get_doc)
  • Query Builder (frappe.qb)
  • Raw SQL when you need it
  • Database transactions (commit/rollback)
  • Indexes & performance tuning
  • Full-text search
  • Support for both MariaDB and PostgreSQL

5. Scripts

  • Client Script (JS) — form events, field validation, UI customization, API calls
  • Server Script (Python) — business logic, scheduled tasks, event-based logic
  • System Console — execute Python/JS, debug & test, developer tools

6. Reports

  • Query Report — drag-and-drop fields, filters, groups, aggregations, charts & pivot
  • Script Report — custom Python logic with dynamic data
  • Reports support charts, pivots, indicators, export (Excel/CSV/PDF), scheduling & sharing

7. Print Formats

Build documents using Jinja templates — supports HTML/PDF output, dynamic data, company letterheads, custom styles, and watermarks/signatures.

8. Jinja Templates

Jinja is the templating engine at the heart of Frappe's UI — used in Print Formats, Email Templates, Web Pages, and List/Form Views. Supports variables, filters, control statements, inheritance, and macros/includes.

9. REST APIs & Webhooks

Every DocType gets REST endpoints automatically — list, get, create, update, delete, with filtering, pagination, and field selection:

GET /api/resource/Customer?fields=["name","email"]&filters=["disabled",0]
Enter fullscreen mode Exit fullscreen mode

Webhooks let you fire real-time notifications on document events, triggered securely and reliably.

10. Document APIs & Methods

Common patterns you'll use constantly in server scripts:

frappe.new_doc(...)
frappe.get_doc(...)
frappe.get_list(...)
frappe.get_all(...)
frappe.get_cached_doc(...)
frappe.db.exists(...)
frappe.delete_doc(...)

doc.insert()
doc.save()
doc.submit()
doc.cancel()
doc.append(...)
doc.reload()
doc.db_set(...)
Enter fullscreen mode Exit fullscreen mode

11. Desk

The modern workspace for your team: role-based desk, custom workspaces, modules, shortcuts, notifications, to-do/kanban/calendar views, real-time updates, search & filters, and custom fields/views.

12. Workflows

Automate business processes with workflow states, transitions, state-based permissions, transition conditions, approve/reject actions, email/notification triggers, multi-level approvals, and role-based access.

13. Profiling, Monitoring & Logging

Built-in profiler,
request profiling,
error logs,
background job logs,
scheduler logs,
slow query analysis,
performance monitoring,
site health monitoring — essential for debugging in production.

14. Social Logins & OAuth

Out of the box: Google Login, GitHub Login, LDAP Authentication, OAuth 2.0 Providers, SAML Authentication, OpenID Connect (OIDC), and custom OAuth providers.

15. Fixtures

Fixtures allow you to export customizations from one site and ship them with your application.
Custom Fields
Property Setters
Client Scripts
Server Scripts
Print Formats
Workspaces
Reports
Roles
Notification Templates

fixtures = [
    "Custom Field",
    "Property Setter",
    "Client Script"
]
Enter fullscreen mode Exit fullscreen mode

Export fixtures:

bench export-fixtures
Enter fullscreen mode Exit fullscreen mode

Why this matters:
these fourteen building blocks are what let teams ship scalable, secure business applications faster — with real-time integrations, automation, and enterprise-grade security baked in.


Part 3: Framework Internals — What Happens Under the Hood

This is the part that made Frappe click for me — understanding the actual architecture behind the abstractions.

Request Lifecycle

Every request flows through a clear, layered pipeline:

Browser
   ↓
Nginx
   ↓
Gunicorn
   ↓
Frappe Framework
   ↓
Authentication
   ↓
Permission Check
   ↓
Controller (Python)
   ↓
Business Logic
   ↓
ORM / Query Builder
   ↓
PostgreSQL / MariaDB
   ↓
Response (JSON / HTML)
Enter fullscreen mode Exit fullscreen mode
  • Browser — user makes a request
  • Nginx — handles static files, SSL, and reverse proxying
  • Gunicorn — WSGI server that receives the request
  • Frappe Framework — routes the request to the application
  • Authentication — user is authenticated (session/token/OAuth)
  • Permission Check — permissions are validated
  • Controller (Python) — request is handled by the appropriate controller
  • Business Logic — application logic is executed
  • ORM / Query Builder — database operations (ORM or Query Builder)
  • PostgreSQL / MariaDB — data is read from or written to the database
  • Response (JSON / HTML) — response is sent back to the browser

Redis (In-Memory Store)

Redis is the central storage for queues, cache, sessions, locks, and realtime events. It handles: cache, session storage, the Redis Queue (RQ), pub/sub for realtime events, job metadata, distributed locks, rate limiting, and background job status.

Background Jobs (RQ)

RQ (Redis Queue) manages asynchronous tasks so users don't wait around for long-running operations:

frappe.enqueue(...)
   ↓
RQ Creates Job
   ↓
Redis Queue
Enter fullscreen mode Exit fullscreen mode

Typical background jobs: email, SMS, WhatsApp, notifications, report generation, PDF generation, import/export, webhook calls, API sync, and scheduler jobs. Jobs are stored in Redis, and workers fetch and execute them.

Workers

Workers are long-running Python processes that monitor Redis queues, fetch jobs, and execute them asynchronously. There are three tiers:

Queue Worker Examples
Short Queue Worker (Short) Notifications, email, SMS, small updates
Default Queue Worker (Default) Business logic, API calls, data processing
Long Queue Worker (Long) Reports, imports, exports, PDF, AI jobs

Important: without running workers, queued jobs remain in Redis and are never executed.

Scheduler Events

The scheduler checks scheduled events defined in hooks.py, creates background jobs, and places them into Redis queues:

hooks.py (scheduler_events)
   ↓
Scheduler Process (bench schedule)
   ↓
Checks Schedule
   ↓
Calls frappe.enqueue()
   ↓
Redis Queue (RQ)
   ↓
Worker
   ↓
Execute Task
Enter fullscreen mode Exit fullscreen mode

Schedule types supported: Hourly, Daily, Weekly, Monthly, and Cron (custom).

Crucially: the scheduler never executes jobs itself — it only enqueues them. Actual execution is always the worker's job.

Realtime (Socket.IO)

Realtime communication between server and clients — live notifications, progress bars, desk updates, and chat/collaboration — all powered by Socket.IO and Redis Pub/Sub.

Authentication & Session

Handles user authentication and session management: session management, API keys, token authentication (REST API), OAuth/social login, and role-based access — supporting multiple authentication methods.

Cache

Caching improves performance and reduces database load, via Redis cache, metadata cache, session cache, and document cache — for frequently accessed data.

Monitoring & Logging

Tools to monitor performance, errors, and system health: Error Log DocType, system logs (bench logs), slow query logs, background job logs, scheduler logs, bench doctor (health check), and the built-in profiler.

Database Layer

Handles all database operations and provides an abstraction over SQL: PostgreSQL/MariaDB support, Frappe ORM, Query Builder, transactions (commit/rollback), row-level locking, indexes & performance, and migrations (bench migrate).

Frappe Queue Management Architecture — Two Flows

A. User-Triggered Flow

User Click / Action (Generate Report, Send Email, etc.)
   ↓
frappe.enqueue() — Add job to queue
   ↓
Redis Queue (RQ) — Job stored in Redis
   ↓
Routed to Short / Default / Long Queue
   ↓
Worker executes the background job
   ↓
Database updated / Notification sent
   ↓
Job Completed (result stored or sent back)
Enter fullscreen mode Exit fullscreen mode

B. Scheduled Layer

Time Reached (as per schedule)
   ↓
Scheduler Process (bench schedule) checks hooks.py Schedule Events
   ↓
Calls frappe.enqueue() — creates background job
   ↓
Redis Queue (RQ)
   ↓
Worker
   ↓
Execute Scheduled Task
   ↓
Job Completed
Enter fullscreen mode Exit fullscreen mode

Example scheduled events: hourly tasks, daily reports, weekly cleanups, monthly jobs, custom cron jobs.

Common Bench Commands (Internals-Focused)

Command Description
bench start Start all services (web, worker, scheduler, etc.)
bench stop Stop all services
bench restart Restart all services
bench status Check status of all services
bench worker Start worker for default queue
bench worker --queue short Start worker for short queue (fast tasks)
bench worker --queue long Start worker for long queue (heavy tasks)
bench schedule Start scheduler process
bench migrate Run database migrations
bench doctor Check bench health and system status
bench show-pending-jobs Show pending jobs in all queues
bench purge-jobs Purge completed/failed jobs from queues
bench backup Create a backup of the site
bench console Open Frappe console

Official Resources

Frappe Official Website: https://frappe.io/
Frappe Framework Documentation: https://docs.frappe.io/framework
ERPNext Documentation: https://docs.erpnext.com
Frappe GitHub Repository: https://github.com/frappe/frappe
ERPNext GitHub Repository: https://github.com/frappe/erpnext

Wrapping Up

Frappe's real power isn't any single feature — it's how metadata (DocTypes), events (hooks), and infrastructure (Redis, RQ, workers, scheduler) compose together into a cohesive system. Once you understand the request lifecycle and the queue architecture, the rest of the framework — controllers, permissions, workflows, reports — starts to feel like straightforward configuration on top of a solid foundation.

If you're building your first Frappe app, my suggestion:
start with bench new-app,
get a simple DocType working end-to-end (form → hook → API),
go read about workers and the scheduler.
The internals will make a lot more sense once you've felt the "front door" of the framework first.

Happy building! 🚀

If this was useful, drop a comment with what you're building on Frappe — always curious to see what people ship with it.

Top comments (0)