Skip to main content
Frappe Helpdesk is built on a modern full-stack architecture combining the Frappe Framework backend with a Vue 3 frontend. This document provides an overview of the system design, key components, and architectural patterns.

Technology Stack

Backend

  • Frappe Framework: Python-based full-stack web application framework
  • Python: 3.14+ with type annotations
  • MariaDB: 10.8+ for relational data storage
  • Redis: Caching, background jobs, and real-time updates
  • Socket.IO: WebSocket server for real-time communication

Frontend

  • Vue 3: Progressive JavaScript framework with Composition API
  • TypeScript: Type-safe development
  • Vite: Fast build tool and dev server
  • Frappe UI: Vue-based UI component library
  • Tailwind CSS: Utility-first CSS framework
  • Pinia: State management
  • Vue Router: Client-side routing

Additional Technologies

  • Textblob: Natural language processing for text analysis
  • Socket.IO Client: Real-time updates in the frontend
  • Tiptap: Rich text editor based on ProseMirror
  • Lucide Icons: Modern icon system via unplugin-icons

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Client Browser                        │
│  ┌────────────────────────────────────────────────────────┐ │
│  │         Vue 3 SPA (desk/)                              │ │
│  │  - Components, Pages, Stores                          │ │
│  │  - Vue Router, Pinia                                  │ │
│  │  - Tailwind CSS, Frappe UI                            │ │
│  └────────────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
                        │ HTTP/WebSocket

┌─────────────────────────────────────────────────────────────┐
│                   Frappe Application Server                  │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Web Server (port 8000)                                │ │
│  │  - REST API endpoints (helpdesk/api/)                  │ │
│  │  - Authentication & permissions                        │ │
│  │  - Business logic (helpdesk/utils.py)                  │ │
│  └────────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  SocketIO Server (port 9000)                           │ │
│  │  - Real-time updates                                   │ │
│  │  - Live notifications                                  │ │
│  └────────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Background Workers                                    │ │
│  │  - Async job processing                                │ │
│  │  - Email sending                                       │ │
│  │  - Scheduled tasks                                     │ │
│  └────────────────────────────────────────────────────────┘ │
└───────────────┬───────────────────────┬─────────────────────┘
                │                       │
                ▼                       ▼
        ┌──────────────┐        ┌─────────────┐
        │   MariaDB    │        │    Redis    │
        │  (Database)  │        │  (Cache/Q)  │
        └──────────────┘        └─────────────┘

Project Structure

Backend Structure (helpdesk/)

helpdesk/
├── api/                      # REST API endpoints
│   ├── agent.py             # Agent management APIs
│   ├── article.py           # Knowledge base APIs
│   ├── ticket.py            # Ticket operations
│   ├── dashboard.py         # Dashboard data APIs
│   ├── doc.py               # Generic document operations
│   ├── search.py            # Search functionality
│   ├── settings/            # Settings APIs
│   └── ...
├── helpdesk/                # DocTypes (data models)
│   └── doctype/
│       ├── hd_ticket/       # Ticket DocType
│       ├── hd_agent/        # Agent DocType
│       ├── hd_article/      # Article DocType
│       ├── hd_customer/     # Customer DocType
│       ├── hd_service_level_agreement/
│       └── ...              # 20+ DocTypes
├── extends/                 # Extensions to core doctypes
│   └── assignment_rule.py
├── overrides/               # Custom behavior overrides
│   ├── contact.py
│   └── email_account.py
├── mixins/                  # Reusable mixins
├── patches/                 # Database migration patches
├── setup/                   # Installation scripts
├── templates/               # Jinja2 templates
├── public/                  # Static assets
├── www/                     # Web pages
├── hooks.py                 # App hooks and configuration
├── utils.py                 # Utility functions
├── search.py                # Full-text search implementation
├── auth.py                  # Authentication logic
└── activation.py            # Site activation

Frontend Structure (desk/)

desk/
├── src/
│   ├── components/           # Reusable Vue components
│   │   ├── desk/            # Desktop-specific components
│   │   ├── global/          # Globally available components
│   │   ├── icons/           # Custom icon components
│   │   ├── settings/        # Settings UI components
│   │   └── ...
│   ├── pages/               # Page-level components
│   │   ├── auth/            # Authentication pages
│   │   ├── desk/            # Main desk pages
│   │   │   ├── ticket/      # Ticket views
│   │   │   ├── agent/       # Agent management
│   │   │   ├── knowledge-base/
│   │   │   └── settings/
│   │   └── ...
│   ├── stores/              # Pinia state stores
│   │   ├── agent.ts         # Agent state
│   │   ├── settings.ts      # Settings state
│   │   └── ...
│   ├── router/              # Vue Router configuration
│   │   └── index.ts
│   ├── composables/         # Vue composables
│   │   ├── conversation.ts
│   │   ├── filter.ts
│   │   └── ...
│   ├── types/               # TypeScript type definitions
│   ├── assets/              # Images, fonts, etc.
│   ├── App.vue              # Root component
│   ├── main.js              # Application entry point
│   ├── utils.ts             # Utility functions
│   ├── types.ts             # Type definitions
│   ├── socket.ts            # Socket.IO client setup
│   └── dayjs.ts             # Date/time configuration
├── public/                  # Static public assets
├── index.html               # HTML entry point
├── vite.config.js           # Vite configuration
├── tailwind.config.js       # Tailwind CSS configuration
├── tsconfig.json            # TypeScript configuration
└── package.json             # Dependencies

Core Components

DocTypes (Data Models)

Frappe uses DocTypes as the foundation for data modeling. Key DocTypes in Helpdesk:
  • HD Ticket: Core ticket entity with subject, description, status, priority
  • HD Agent: Agent profiles and permissions
  • HD Customer: Customer information and contact details
  • HD Article: Knowledge base articles
  • HD Service Level Agreement: SLA definitions and tracking
  • HD Ticket Comment: Conversation threads
  • HD Notification: User notifications
  • HD Saved Reply: Templated responses
  • HD Assignment Rule: Auto-assignment configuration
DocTypes define:
  • Field schema and validation
  • Permissions and access control
  • Server-side business logic
  • Database structure

API Layer

API endpoints are Python functions decorated with @frappe.whitelist() located in helpdesk/api/:
# Example: helpdesk/api/ticket.py
import frappe

@frappe.whitelist()
def assign_ticket_to_agent(ticket_id: str, agent_id: str):
    """Assign a ticket to an agent"""
    ticket = frappe.get_doc("HD Ticket", ticket_id)
    ticket.assign_to(agent_id)
    ticket.save()
    return ticket
API endpoints handle:
  • Data validation
  • Business logic execution
  • Permission checks
  • Database operations

Frontend Data Fetching

The frontend uses Frappe UI’s resource composables for data fetching:
// List of documents
const tickets = createListResource({
  doctype: "HD Ticket",
  filters: { status: "Open" },
  fields: ["name", "subject", "modified"],
  auto: true,
});

// Single document
const ticket = createDocumentResource({
  doctype: "HD Ticket",
  name: ticketId,
  auto: true,
});

// API call
const assignTicket = createResource({
  url: "helpdesk.api.ticket.assign_ticket_to_agent",
  onSuccess(data) {
    // Handle success
  },
});

Real-Time Updates

Socket.IO enables real-time features: Backend (helpdesk/utils.py):
import frappe

def notify_ticket_update(ticket_id):
    frappe.publish_realtime(
        event="ticket_updated",
        message={"ticket_id": ticket_id},
        user=frappe.session.user
    )
Frontend (desk/src/socket.ts):
import { io } from "socket.io-client";

const socket = io("http://localhost:9000");

socket.on("ticket_updated", (data) => {
  // Refresh ticket data
  ticket.reload();
});

Key Design Patterns

Frappe ORM

Frappe provides an ORM for database operations:
# Get a document
ticket = frappe.get_doc("HD Ticket", "TKT-0001")

# Query with filters (preferred method)
from frappe.query_builder import DocType

Ticket = DocType("HD Ticket")
tickets = frappe.qb.get_query(
    Ticket.name,
    Ticket.subject,
    Ticket.status,
    filters=[(Ticket.status == "Open")],
    ignore_permissions=False
).run(as_dict=True)

# Create new document
new_ticket = frappe.get_doc({
    "doctype": "HD Ticket",
    "subject": "Need help",
    "description": "Description here",
})
new_ticket.insert()

Permission System

Frappe’s role-based permission system is extended in helpdesk/hooks.py:
has_permission = {
    "HD Ticket": "helpdesk.helpdesk.doctype.hd_ticket.hd_ticket.has_permission",
}

permission_query_conditions = {
    "HD Ticket": "helpdesk.helpdesk.doctype.hd_ticket.hd_ticket.permission_query",
}
Custom permission logic:
def has_permission(doc, ptype, user):
    # Agents can view assigned tickets
    if ptype == "read":
        if doc.assigned_to == user:
            return True
    
    # Customers can view their own tickets
    if doc.raised_by == user:
        return True
    
    return False

Component Composition

Vue components follow the Composition API pattern:
<script setup lang="ts">
import { ref, computed } from "vue";
import { createDocumentResource } from "frappe-ui";
import LucideTicket from "~icons/lucide/ticket";

interface Props {
  ticketId: string;
}

const props = defineProps<Props>();

const ticket = createDocumentResource({
  doctype: "HD Ticket",
  name: props.ticketId,
  auto: true,
});

const isOpen = computed(() => ticket.doc?.status === "Open");

function closeTicket() {
  ticket.setValue("status", "Closed");
  ticket.save();
}
</script>

<template>
  <div class="bg-surface-white border border-outline-gray-2 rounded-lg p-4">
    <div class="flex items-center gap-2">
      <LucideTicket class="size-4 text-ink-gray-6" />
      <h2 class="text-lg text-ink-gray-9 font-semibold">
        {{ ticket.doc?.subject }}
      </h2>
    </div>
    <button
      v-if="isOpen"
      @click="closeTicket"
      class="mt-2 px-4 py-2 bg-surface-gray-2 rounded"
    >
      Close Ticket
    </button>
  </div>
</template>

State Management

Pinia stores manage global state:
// stores/agent.ts
import { defineStore } from "pinia";
import { createResource } from "frappe-ui";

export const useAgentStore = defineStore("agent", () => {
  const currentAgent = ref(null);
  
  const agentResource = createResource({
    url: "helpdesk.api.agent.get_current_agent",
    auto: true,
    onSuccess(data) {
      currentAgent.value = data;
    },
  });
  
  return {
    currentAgent,
    refresh: agentResource.reload,
  };
});

Search Architecture

Frappe Helpdesk implements full-text search using SQLite FTS5: Backend (helpdesk/search.py):
  • Index building for tickets and articles
  • Natural language query processing
  • Relevance ranking
  • Background index updates
Configuration (helpdesk/hooks.py):
sqlite_search = ["helpdesk.search_sqlite.HelpdeskSearch"]

scheduler_events = {
    "all": [
        "helpdesk.search.build_index_if_not_exists",
    ],
}

Background Jobs

Frappe’s scheduler handles background tasks:
# hooks.py
scheduler_events = {
    "daily": [
        "helpdesk.helpdesk.doctype.hd_ticket.hd_ticket.close_tickets_after_n_days"
    ],
}
Manual job enqueueing:
frappe.enqueue(
    "helpdesk.utils.send_notification_email",
    queue="default",
    timeout=300,
    ticket_id=ticket_id,
)

Build and Deploy Pipeline

Development Build

# Frontend dev server with HMR
cd desk
yarn dev

Production Build

Configured in desk/vite.config.js:
export default defineConfig({
  plugins: [
    frappeui({
      buildConfig: {
        outDir: `../helpdesk/public/desk`,
        emptyOutDir: true,
        indexHtmlPath: "../helpdesk/www/helpdesk/index.html",
      },
    }),
  ],
});
Build command:
bench build --app helpdesk
This compiles Vue components and outputs:
  • Static assets to helpdesk/public/desk/
  • HTML entry point to helpdesk/www/helpdesk/index.html

Security Considerations

Authentication

  • Session-based authentication via Frappe Framework
  • Custom auth hooks in helpdesk/auth.py
  • JWT support for API access

Authorization

  • Role-based access control (RBAC)
  • Document-level permissions
  • Field-level permission rules
  • Custom permission handlers

Data Validation

  • Server-side validation in DocType controllers
  • Frontend validation using Zod schemas
  • SQL injection prevention via ORM
  • XSS protection with sanitize-html

Performance Optimization

Backend

  • Redis caching for frequently accessed data
  • Database query optimization with indexes
  • Background job processing for heavy tasks
  • Connection pooling for database

Frontend

  • Code splitting with Vite
  • Lazy loading of routes
  • Virtual scrolling for large lists
  • Debounced search inputs
  • Progressive Web App (PWA) support

Extensibility

Custom DocTypes

Create new DocTypes through the Frappe UI or JSON definition.

API Extensions

Add new endpoints in helpdesk/api/ or helpdesk/extends/.

Frontend Plugins

Register custom components and routes:
// main.js
app.component("CustomWidget", CustomWidget);

Hooks

Extend behavior via helpdesk/hooks.py:
doc_events = {
    "HD Ticket": {
        "before_save": "helpdesk.custom.validate_ticket",
        "on_update": "helpdesk.custom.notify_watchers",
    },
}

Testing Strategy

Backend Tests

Frappe’s unittest-based testing:
# helpdesk/helpdesk/doctype/hd_ticket/test_hd_ticket.py
import frappe
import unittest

class TestHDTicket(unittest.TestCase):
    def test_ticket_creation(self):
        ticket = frappe.get_doc({
            "doctype": "HD Ticket",
            "subject": "Test",
        })
        ticket.insert()
        self.assertTrue(ticket.name)

Frontend Tests

Not currently implemented in the repository, but can be added with Vitest.

Next Steps

Additional Resources

Build docs developers (and LLMs) love