Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/frappe/frappe/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through creating a simple “Library Management” app to demonstrate Frappe’s core features. By the end, you’ll have a working application with data entry forms, list views, and a REST API.
Prerequisites: Complete the installation guide and have a Frappe site running locally.

Create your first app

Apps in Frappe are Python packages that contain your business logic, DocTypes, and customizations.
1

Create a new app

Use bench to scaffold a new application:
cd ~/frappe-bench
bench new-app library_management
You’ll be prompted for:
  • App title: Library Management
  • App description: Manage library books and members
  • Publisher: Your name
  • Email: Your email
  • License: MIT
This creates a new app directory at ~/frappe-bench/apps/library_management with the following structure:
library_management/
├── library_management/
│   ├── __init__.py
│   ├── hooks.py          # App configuration and hooks
│   ├── modules.txt       # List of modules
│   └── public/           # Static assets
├── setup.py
└── requirements.txt
2

Install the app on your site

bench --site frappe.localhost install-app library_management
This installs the app and makes it available on your site.
3

Add a module

Create a module to organize your DocTypes:
echo "Library Management" > apps/library_management/library_management/modules.txt

Create your first DocType

DocTypes are Frappe’s metadata models. They define the structure of your data and automatically generate forms, APIs, and database tables.
1

Create the Article DocType

We’ll create an Article DocType to represent library books:
  1. Open your browser and go to http://frappe.localhost:8000/app
  2. In the search bar (Ctrl+K or ⌘K), type “New DocType”
  3. Fill in the following:
  • Name: Article
  • Module: Library Management
  • Is Submittable: Unchecked (for now)
Add these fields by clicking “Add Row” in the Fields table:
LabelTypeOptionsMandatory
Article NameData-Yes
ImageAttach Image-No
AuthorData-No
DescriptionText Editor-No
ISBNData-No
StatusSelectIssued\nAvailableYes
PublisherData-No
  1. Set Naming to “field:article_name”
  2. Click Save
2

Create your first article

  1. Search for “Article List” in the search bar
  2. Click New
  3. Fill in the details:
    • Article Name: The Alchemist
    • Author: Paulo Coelho
    • Status: Available
  4. Click Save
You now have a working CRUD interface with zero code! Frappe automatically generated the form, list view, and database schema.

Add server-side logic

Let’s add Python code to validate and process Article documents.
1

Create the Python controller

Create a file at apps/library_management/library_management/library_management/doctype/article/article.py:
# Copyright (c) 2024, Your Name and contributors
# For license information, please see license.txt

import frappe
from frappe.model.document import Document

class Article(Document):
    # Validation method - runs before saving
    def before_save(self):
        if not self.isbn:
            frappe.msgprint("Consider adding an ISBN number")
    
    # Validation method - can throw errors
    def validate(self):
        if self.status == "Issued" and not self.publisher:
            frappe.throw("Publisher is required for issued articles")
    
    # Event handler - runs after saving
    def after_insert(self):
        frappe.msgprint(f"Article {self.article_name} has been created!")
Frappe automatically calls these methods at the right time in the document lifecycle:
  • before_save() - Before saving to database
  • validate() - During validation
  • after_insert() - After creating a new document
  • on_update() - After updating
  • on_trash() - Before deletion
2

Test the validation

  1. Try creating a new Article with Status = “Issued” but no Publisher
  2. You should see an error message
  3. Fill in the Publisher field and save successfully

Add a whitelisted API method

Create a custom API endpoint that can be called from the frontend or external systems.
1

Add an API method

Add this method to your article.py file:
@frappe.whitelist()
def get_articles_by_status(status):
    """Get all articles with the given status"""
    articles = frappe.get_all(
        "Article",
        filters={"status": status},
        fields=["name", "article_name", "author", "publisher"]
    )
    return articles
The @frappe.whitelist() decorator makes this method accessible via HTTP.
2

Test the API

Open your browser console and run:
frappe.call({
    method: "library_management.library_management.doctype.article.article.get_articles_by_status",
    args: {
        status: "Available"
    },
    callback: function(r) {
        console.log(r.message);
    }
});
Or test with curl:
curl -X POST http://frappe.localhost:8000/api/method/library_management.library_management.doctype.article.article.get_articles_by_status \
  -H "Content-Type: application/json" \
  -d '{"status": "Available"}'

Use the automatic REST API

Frappe automatically generates REST API endpoints for all DocTypes.
# Get all articles
curl http://frappe.localhost:8000/api/resource/Article
For authenticated requests, you’ll need to include API keys or session cookies. See REST API authentication for details.

Add a child table

Let’s create a Library Member DocType with a child table for tracking borrowed articles.
1

Create Library Member DocType

Create a new DocType named “Library Member” with these fields:
LabelTypeOptions
Full NameData-
EmailData-
PhoneData-
Borrowed ArticlesTableLibrary Transaction
2

Create Library Transaction child DocType

Create another DocType named “Library Transaction”:
  • Check Is Child Table
  • Add these fields:
    LabelTypeOptions
    ArticleLinkArticle
    Date BorrowedDate-
    Date ReturnedDate-
3

Test the relationship

  1. Create a new Library Member
  2. In the “Borrowed Articles” table, add a row
  3. Select an Article and set the Date Borrowed
  4. Save
The child records are automatically saved with the parent!

Add client-side scripting

Customize form behavior with JavaScript.
1

Create a client script

Create article.js in the same directory as article.py:
frappe.ui.form.on('Article', {
    // Runs when form loads
    refresh: function(frm) {
        // Add a custom button
        frm.add_custom_button('Check Availability', function() {
            if (frm.doc.status === 'Available') {
                frappe.msgprint('This article is available!');
            } else {
                frappe.msgprint('This article is currently issued.');
            }
        });
    },
    
    // Runs when status field changes
    status: function(frm) {
        if (frm.doc.status === 'Issued') {
            frappe.msgprint('Remember to update the return date!');
        }
    }
});
2

Clear cache and reload

bench --site frappe.localhost clear-cache
Refresh your browser to see the custom button and behavior.

Query the database

Frappe provides multiple ways to query your data.
# Get all available articles
articles = frappe.get_all(
    "Article",
    filters={"status": "Available"},
    fields=["name", "article_name", "author"],
    order_by="article_name asc"
)

Background jobs

For long-running tasks, use Frappe’s background job queue.
import frappe
from frappe.utils.background_jobs import enqueue

def send_overdue_notifications():
    """Send notifications for overdue articles"""
    # Long running task...
    pass

# Queue the job
enqueue(
    send_overdue_notifications,
    queue='long',
    timeout=300
)

What you’ve learned

Congratulations! You’ve built a working Frappe application with:

DocTypes

Metadata-driven data models

Server-side logic

Python controllers and validation

REST API

Automatic and custom endpoints

Client scripting

Form customization with JavaScript

Next steps

Architecture overview

Understand Frappe’s architecture

DocType deep dive

Master DocTypes and field types

Permissions

Set up role-based permissions

Hooks

Extend Frappe with hooks
Join the community! Ask questions and share your apps on the Frappe Forum.