UNPKG

frappe-js-client

Version:

Next-generation TS/JS client for Frappe REST APIs, built on axios for robust, type-safe integration.

498 lines (393 loc) 12.7 kB
# Frappe JS Client [![npm version](https://badge.fury.io/js/frappe-js-client.svg)](https://badge.fury.io/js/frappe-js-client) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) A modern, type-safe TypeScript/JavaScript client for Frappe Framework applications. Built with Axios for robust HTTP communication and comprehensive error handling. ## 📋 Table of Contents - [Frappe JS Client](#frappe-js-client) - [📋 Table of Contents](#-table-of-contents) - [✨ Features](#-features) - [📦 Installation](#-installation) - [🚀 Quick Start](#-quick-start) - [📚 API Reference](#-api-reference) - [FrappeApp](#frappeapp) - [Parameters](#parameters) - [Methods](#methods) - [🔐 Authentication](#-authentication) - [Basic Authentication](#basic-authentication) - [Token-Based Authentication](#token-based-authentication) - [🗄️ Database Operations](#️-database-operations) - [Document Operations](#document-operations) - [Advanced Filtering](#advanced-filtering) - [📁 File Operations](#-file-operations) - [File Upload](#file-upload) - [File Download](#file-download) - [🌐 API Calls](#-api-calls) - [HTTP Methods](#http-methods) - [🛡️ Error Handling](#️-error-handling) - [🔧 TypeScript Support](#-typescript-support) - [Custom Document Types](#custom-document-types) - [API Response Types](#api-response-types) - [📖 Examples](#-examples) - [Complete Application Example](#complete-application-example) - [React Hook Example](#react-hook-example) - [🤝 Contributing](#-contributing) - [Development Setup](#development-setup) - [📄 License](#-license) - [🆘 Support](#-support) ## ✨ Features - 🔐 **Authentication**: Multiple auth methods (username/password, OTP, token-based) - 🗄️ **Database Operations**: Full CRUD operations with filtering, sorting, and pagination - 📁 **File Management**: Upload, download, and manage files with progress tracking - 🌐 **API Integration**: Direct access to Frappe API endpoints - 🛡️ **Type Safety**: Full TypeScript support with comprehensive type definitions - ⚡ **Performance**: Built on Axios with optimized request handling - 🔄 **Error Handling**: Standardized error responses and handling - 🎯 **Permission System**: Built-in permission checking and management ## 📦 Installation ```bash npm install frappe-js-client # or yarn add frappe-js-client # or pnpm add frappe-js-client ``` ## 🚀 Quick Start ```typescript import { FrappeApp } from 'frappe-js-client' // Initialize the client const app = new FrappeApp('https://your-frappe-site.com') // Authenticate const auth = app.auth() await auth.loginWithUsernamePassword({ username: 'admin', password: 'password', device: 'desktop', }) // Database operations const db = app.db() const users = await db.getDocList('User', { fields: ['name', 'email', 'first_name'], filters: [['user_type', '=', 'System User']], limit: 10, }) // File operations const file = app.file() const uploadResult = await file.uploadFile(fileBlob, { isPrivate: true, folder: 'Home/Documents', }) // API calls const call = app.call() const response = await call.get('frappe.ping') ``` ## 📚 API Reference ### FrappeApp The main class for interacting with a Frappe instance. ```typescript class FrappeApp { constructor(url: string, tokenParams?: TokenParams, name?: string, customHeaders?: object) } ``` #### Parameters - `url` (string): The base URL of your Frappe instance - `tokenParams` (optional): Configuration for token-based authentication - `name` (optional): Name for the app instance (defaults to 'FrappeApp') - `customHeaders` (optional): Custom headers to include in all requests #### Methods - `auth()`: Returns authentication handler - `db()`: Returns database operations handler - `file()`: Returns file operations handler - `call()`: Returns API call handler - `client()`: Returns client operations handler - `perms()`: Returns permission handler ## 🔐 Authentication ### Basic Authentication ```typescript const auth = app.auth() // Login with username and password const response = await auth.loginWithUsernamePassword({ username: 'admin', password: 'password', device: 'desktop', }) // Login with OTP const otpResponse = await auth.loginWithUsernamePassword({ otp: '123456', tmp_id: 'temp123', device: 'mobile', }) // Get current user const currentUser = await auth.getLoggedInUser() // Logout await auth.logout() // Password reset await auth.forgetPassword('admin@example.com') ``` ### Token-Based Authentication ```typescript // Initialize with token authentication const app = new FrappeApp('https://your-site.com', { useToken: true, token: () => localStorage.getItem('token'), type: 'Bearer', }) ``` ## 🗄️ Database Operations ### Document Operations ```typescript const db = app.db() // Get a single document const user = await db.getDoc('User', 'administrator') // Get document list with filters const tasks = await db.getDocList('Task', { fields: ['name', 'subject', 'status', 'assigned_to'], filters: [['status', '=', 'Open']], orderBy: { field: 'creation', order: 'desc' }, limit: 20, }) // Create a new document const newTask = await db.createDoc('Task', { subject: 'New Task', status: 'Open', description: 'Task description', }) // Update a document await db.updateDoc('Task', newTask.name, { status: 'Completed', completed_on: new Date().toISOString(), }) // Delete a document await db.deleteDoc('Task', newTask.name) // Get the last document const lastTask = await db.getLastDoc('Task', { filters: [['status', '=', 'Open']], }) ``` ### Advanced Filtering ```typescript // Complex filters const filteredDocs = await db.getDocList('Task', { filters: [ ['status', '=', 'Open'], ['priority', 'in', ['High', 'Medium']], ['creation', '>=', '2024-01-01'], ], orFilters: [ ['assigned_to', '=', 'admin'], ['owner', '=', 'admin'], ], groupBy: 'status', limit: 50, }) ``` ## 📁 File Operations ### File Upload ```typescript const file = app.file() // Basic file upload const uploadResult = await file.uploadFile(fileBlob, { isPrivate: true, folder: 'Home/Documents', }) // Upload with progress tracking const result = await file.uploadFile(fileBlob, { isPrivate: false }, (uploaded, total) => { const progress = (uploaded / total) * 100 console.log(`Upload progress: ${progress.toFixed(2)}%`) }) // Upload with custom metadata const response = await file.uploadFile(fileBlob, { doctype: 'Task', docname: 'TASK-001', fieldname: 'attachment', otherData: { category: 'Important', tags: 'urgent,review', }, }) ``` ### File Download ```typescript const downloader = new FrappeFileDownload(axiosInstance) const fileBlob = await downloader.downloadFile('https://your-site.com/files/document.pdf') ``` ## 🌐 API Calls ### HTTP Methods ```typescript const call = app.call() // GET request const response = await call.get('frappe.ping') // GET with parameters const users = await call.get('frappe.user.get_users', { filters: { user_type: 'System User' }, limit: 10, }) // POST request const result = await call.post('frappe.handler', { data: { key: 'value' }, }) // PUT request await call.put('frappe.user.update_prefs', { user: 'admin', preferences: { theme: 'dark' }, }) // DELETE request await call.delete('frappe.user.delete_user', { user: 'test_user', }) ``` ## 🛡️ Error Handling The SDK provides standardized error handling with detailed error information: ```typescript try { await db.createDoc('Task', { subject: 'New Task' }) } catch (error) { const frappeError = error as Error console.error(`HTTP Status: ${frappeError.httpStatus}`) console.error(`Message: ${frappeError.message}`) console.error(`Exception: ${frappeError.exception}`) // Handle specific error types switch (frappeError.exception) { case 'ValidationError': // Handle validation errors break case 'PermissionError': // Handle permission errors break case 'DoesNotExistError': // Handle not found errors break } } ``` ## 🔧 TypeScript Support ### Custom Document Types ```typescript import { FrappeDoc } from 'frappe-js-client' // Define custom document interface interface Task extends FrappeDoc<{ subject: string status: 'Open' | 'In Progress' | 'Completed' priority: 'Low' | 'Medium' | 'High' assigned_to?: string description?: string due_date?: string }> {} // Use typed operations const task = await db.getDoc<Task>('Task', 'TASK-001') const tasks = await db.getDocList<Task>('Task', { filters: [['status', '=', 'Open']], }) ``` ### API Response Types ```typescript interface LoginResponse { message: string home_page: string user: string } const response = await auth.loginWithUsernamePassword<LoginResponse>({ username: 'admin', password: 'password', }) ``` ## 📖 Examples ### Complete Application Example ```typescript import { FrappeApp } from 'frappe-js-client' class TaskManager { private app: FrappeApp private db: any private auth: any constructor(frappeUrl: string) { this.app = new FrappeApp(frappeUrl) this.db = this.app.db() this.auth = this.app.auth() } async initialize() { // Authenticate await this.auth.loginWithUsernamePassword({ username: 'admin', password: 'password', }) } async createTask(subject: string, description?: string) { return await this.db.createDoc('Task', { subject, description, status: 'Open', priority: 'Medium', }) } async getOpenTasks() { return await this.db.getDocList('Task', { fields: ['name', 'subject', 'status', 'priority'], filters: [['status', '=', 'Open']], orderBy: { field: 'creation', order: 'desc' }, }) } async completeTask(taskName: string) { return await this.db.updateDoc('Task', taskName, { status: 'Completed', completed_on: new Date().toISOString(), }) } } // Usage const taskManager = new TaskManager('https://your-frappe-site.com') await taskManager.initialize() const newTask = await taskManager.createTask('Implement new feature') const openTasks = await taskManager.getOpenTasks() await taskManager.completeTask(newTask.name) ``` ### React Hook Example ```typescript import { useState, useEffect } from 'react' import { FrappeApp } from 'frappe-js-client' const useFrappeClient = (url: string) => { const [app] = useState(() => new FrappeApp(url)) const [isAuthenticated, setIsAuthenticated] = useState(false) useEffect(() => { const checkAuth = async () => { try { const auth = app.auth() const user = await auth.getLoggedInUser() setIsAuthenticated(!!user) } catch { setIsAuthenticated(false) } } checkAuth() }, [app]) return { app, isAuthenticated } } ``` ## 🤝 Contributing We welcome contributions! Please see our contributing guidelines: 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ### Development Setup ```bash # Clone the repository git clone https://github.com/dhiashalabi/frappe-js-client.git # Install dependencies npm install # Run tests npm test # Build the project npm run build # Run linting npm run lint ``` ## 📄 License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## 🆘 Support - 📧 **Issues**: [GitHub Issues](https://github.com/dhiashalabi/frappe-js-client/issues) - 📖 **Documentation**: [GitHub Wiki](https://github.com/dhiashalabi/frappe-js-client/wiki) - 💬 **Discussions**: [GitHub Discussions](https://github.com/dhiashalabi/frappe-js-client/discussions) --- Made with ❤️ by the [DHia A. SHalabi](https://github.com/dhiashalabi)