Frappe in one sentence
Frappe is a full-stack, metadata-driven web framework, written in Python on the server and JavaScript on the client, that lets you describe your data models declaratively and get a working application around them almost for free.
Most teams first meet Frappe through ERPNext, the open-source ERP that runs accounting, inventory, manufacturing, and HR for tens of thousands of companies. But ERPNext is just the most famous app built on Frappe. The framework underneath is general purpose, and at Upeosoft we have used it to build systems that look nothing like a traditional ERP: field data collection tools, licensing portals, and industry-specific operations software. Understanding Frappe as a framework, not as ERPNext, is the first step to using it well.
The doctype: Frappe's core abstraction
Everything in Frappe revolves around the doctype. A doctype is a model definition stored as metadata: a list of fields, their types, and their behaviour. When you create a doctype called "Customer," Frappe does a remarkable amount of work for you in one move.
From that single definition you automatically get a database table with the right columns, a REST endpoint at /api/resource/Customer, a list view and a form view in the desk UI, validation, and a permission surface you can configure per role. You did not write a migration, a serializer, a form component, or a controller. That is the metadata-driven idea in practice: the schema is data, and the framework reads that data to build the machinery.
When you need custom logic, each doctype has an optional Python controller class with lifecycle methods such as validate, before_save, on_submit, and on_cancel. That is where your real business rules live, cleanly separated from the plumbing.
- One doctype yields a table, a REST API, list and form UIs, and permissions.
- Field types include links to other doctypes, child tables, select, currency, and more.
- Controller hooks like validate and on_submit hold your business logic.
- Custom Fields and Customize Form let you extend core doctypes without code.
Apps, sites, and the bench
Frappe organises code into apps and data into sites, and the bench CLI ties them together. An app is a Python package with doctypes, code, and assets; frappe itself is an app, erpnext is an app, and your custom code lives in its own app so it stays separate and upgradeable.
A site is a single tenant with its own database and configuration. One bench can host many sites, and each site installs a chosen set of apps. This separation is what makes multi-tenant hosting and clean upgrades possible.
The bench command is your control panel. You use it to create sites, install apps, run database migrations with "bench migrate," build front-end assets, tail logs, and start the whole development stack. In production, bench also manages the web server, the background workers, and the scheduler through a process manager. Learning bench well is most of learning to operate Frappe.
Hooks: extending without forking
The single most important thing to understand for maintainable Frappe work is the hooks system. Every app has a hooks.py file that lets it register into framework and other-app behaviour without editing that other code.
Want to run a function whenever any Sales Invoice is submitted? You add a doc_events entry in your own app's hooks.py pointing at your function. Want to override a whitelisted method, schedule a nightly job, inject a CSS file, or add a permission query condition? All of it is declared in hooks. Your custom app observes and extends; it never patches core in place.
This is what keeps a Frappe deployment upgrade-safe. At Upeosoft, our rule is simple: core apps like frappe and erpnext are read-only, and everything we build goes into a dedicated custom app wired in through hooks. When ERPNext releases a new version, our code rides along instead of fighting the merge.
Batteries included: API, jobs, and permissions
Frappe ships the parts most teams otherwise assemble by hand. There is a complete REST API for every doctype, plus a whitelisting decorator, frappe.whitelist(), that exposes any Python function as a callable endpoint with authentication and permission checks applied.
Background processing is built in. Frappe uses Redis and RQ to run enqueued jobs and a scheduler for periodic tasks, so long-running work like report generation, emailing, or syncing an external system never blocks a request. You call frappe.enqueue with a function path and it runs on a worker.
The permission system is genuinely deep: role-based permissions per doctype, user permissions that scope records to a value, permission query conditions for row-level rules, and field-level read and write control. For business software, where who-can-see-what is often the hardest requirement, having this built in rather than bolted on is a major advantage.
How Frappe compares to a bare framework
If you have built with Django, Rails, or Express, the trade-off is easy to frame. A bare framework gives you maximum control and a blank canvas; you decide every model, endpoint, and form, and you write all of it. That is the right choice when your product is genuinely novel in shape.
Frappe trades some of that control for enormous leverage on the class of software it targets: record-oriented business systems full of forms, lists, workflows, approvals, and reports. On that terrain, the metadata-driven approach means you reach a working, permissioned, API-backed system in a fraction of the time, because you are configuring behaviour rather than coding infrastructure.
The cost is that you learn Frappe's conventions and work within them. Fighting the framework, for instance by bypassing the ORM or hand-editing core, is where teams get into trouble. Used with the grain, it is one of the fastest ways we know to ship serious business software.
When Frappe is the right foundation
Choose Frappe when your problem looks like structured records that people create, review, approve, and report on, especially if permissions and audit trails matter. Inventory, field operations, licensing, member management, service delivery, and anything adjacent to ERP are natural fits, and you gain even more if you also need ERPNext's accounting or stock modules alongside your custom app.
Look elsewhere when you need a real-time collaborative editor, a high-traffic public site that is mostly cacheable content, or a product where you must own every detail of the request lifecycle and schema. Those are not Frappe's strengths, and forcing them tends to erase its advantages.
At Upeosoft we build on ERPNext and Frappe precisely because so much Kenyan business software is record-oriented and permission-heavy. Starting from a mature, open-source foundation lets us spend our engineering time on the parts that are actually unique to each client, instead of rebuilding auth, APIs, and admin for the hundredth time.
