Framework Git

Framework Git

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

Whitelisted Methods

By default, no Python function is reachable over HTTP. To expose one so the browser, a mobile app, or a REST client can call it, decorate it with @frappe.whitelist(). This is how you build custom server endpoints in a Frappe app.

Defining an endpoint

Put the function anywhere in your app (a <app>/api.py module is the convention) and whitelist it:

# library/library/api.py
import frappe

@frappe.whitelist()
def get_overdue_loans(member: str):
    return frappe.get_all(
        "Library Loan",
        filters={"member": member, "status": "Overdue"},
        fields=["name", "book", "due_date"],
    )

It's now callable at /api/method/library.library.api.get_overdue_loans (the full dotted import path).

Whitelisting only authenticates the request; it decides who may call the function at all (a logged-in user, or a guest if allow_guest=True). It does not by itself enforce document permissions. Whether permissions are checked depends on what the function calls inside: frappe.get_all skips permissions, while frappe.get_list and document methods like insert/save/delete enforce them. The example above uses get_all, so it returns every matching Library Loan regardless of who's asking; use get_list if you want per-user filtering.

Argument types

Arguments arrive as strings from HTTP. If you add type annotations, Frappe coerces incoming values to the declared types automatically:

@frappe.whitelist()
def renew_loan(loan: str, days: int = 14):
    # `days` is converted from "14" to the int 14
    ...

Frappe expects type annotations on whitelisted methods by default (governed by the require_type_annotated_api_methods hook). Annotate your parameters.

Restricting HTTP methods

By default a whitelisted function accepts GET, POST, PUT, and DELETE. For anything that changes data, restrict it to write methods so it can't be triggered by a simple link or image tag:

@frappe.whitelist(methods=["POST"])
def create_loan(book: str, member: str):
    doc = frappe.get_doc({
        "doctype": "Library Loan",
        "book": book,
        "member": member,
    })
    doc.insert()
    return doc.name

Allowing guests

To make an endpoint reachable without logging in (e.g. a public signup or webhook), pass allow_guest=True:

@frappe.whitelist(allow_guest=True)
def public_book_count():
    return frappe.db.count("Library Book", {"published": 1})

Guest-accessible endpoints are a common attack surface. Validate every input, never trust the caller, and keep what they can read or do to a minimum.

Whitelisting controller methods

You can whitelist a method on a Document controller. It then runs against a specific document and is callable from the client with the doc's context:

# library/library/doctype/library_loan/library_loan.py
from frappe.model.document import Document

class LibraryLoan(Document):
    @frappe.whitelist()
    def mark_returned(self):
        self.status = "Returned"
        self.return_date = frappe.utils.today()
        self.save()

When called this way, the framework loads the document with check_permission=True (the user must be able to read it) before running the method.

Over HTTP, controller methods go through the built-in run_doc_method endpoint. Pass the doctype as dt, the document name as dn, and the method name:

curl -X POST https://mysite.localhost/api/v2/method/run_doc_method \
  -H "Authorization: token <api_key>:<api_secret>" \
  -H "Content-Type: application/json" \
  -d '{"dt": "Library Loan", "dn": "LOAN-0001", "method": "mark_returned"}'

Calling from the client

frappe.call is available in Desk (the /desk interface) and on Frappe-rendered website pages, since the website JS bundle ships it too. It is not available in a fully custom frontend (a separate SPA, a non-Frappe site); call the method over REST there instead. frm.call is narrower still: it only exists on a form, so you can use it inside Desk form scripts.

From browser JavaScript inside Desk (or a Frappe-rendered website page), use frappe.call for module-level functions:

frappe.call({
  method: "library.library.api.get_overdue_loans",
  args: { member: "MEMBER-0001" },
  callback: (r) => {
    console.log(r.message); // the function's return value
  },
});

The return value is always under r.message.

To call a whitelisted controller method, use the form's helper, which sends the current document along:

frm.call("mark_returned").then((r) => {
  frm.reload_doc();
});

Calling over REST

Whitelisted methods are also plain HTTP endpoints. GET for reads, POST for writes; authenticate with a token or session.

curl -X POST https://mysite.localhost/api/v2/method/library.library.api.create_loan \
  -H "Authorization: token <api_key>:<api_secret>" \
  -H "Content-Type: application/json" \
  -d '{"book": "BOOK-0001", "member": "MEMBER-0001"}'

The response wraps the return value in a message key:

{ "message": "LOAN-0001" }

See the REST API section for authentication and the document CRUD endpoints.

Overriding a method

To replace another app's (or core's) whitelisted method without editing it, map the original dotted path to your replacement with the override_whitelisted_methods hook:

# hooks.py
override_whitelisted_methods = {
    "frappe.client.get_count": "library.overrides.get_count",
}

Override with care. Once a method is mapped, every caller silently runs your replacement instead of the original, which makes failures hard to trace: the code at the dotted path is not what actually ran. Keep the override's behavior close to the original and document why it exists.

See Hooks.

See also

  • Permissions in code: enforcing access inside endpoints.
  • REST API: auth and the built-in resource endpoints.
  • Hooks: overriding and extending whitelisted methods.
Last updated 1 hour ago
Was this helpful?
Thanks!