frappe-js-client
v2.3.7
Published
Next-generation TS/JS client for Frappe REST APIs, built on axios for robust, type-safe integration.
Readme
Frappe JS Client
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
✨ 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
npm install frappe-js-client
# or
yarn add frappe-js-client
# or
pnpm add frappe-js-client🚀 Quick Start
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.
class FrappeApp {
constructor(url: string, tokenParams?: TokenParams, name?: string, customHeaders?: object)
}Parameters
url(string): The base URL of your Frappe instancetokenParams(optional): Configuration for token-based authenticationname(optional): Name for the app instance (defaults to 'FrappeApp')customHeaders(optional): Custom headers to include in all requests
Methods
auth(): Returns authentication handlerdb(): Returns database operations handlerfile(): Returns file operations handlercall(): Returns API call handlerclient(): Returns client operations handlerperms(): Returns permission handler
🔐 Authentication
Basic Authentication
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('[email protected]')Token-Based Authentication
// Initialize with token authentication
const app = new FrappeApp('https://your-site.com', {
useToken: true,
token: () => localStorage.getItem('token'),
type: 'Bearer',
})🗄️ Database Operations
Document Operations
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
// 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
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
const downloader = new FrappeFileDownload(axiosInstance)
const fileBlob = await downloader.downloadFile('https://your-site.com/files/document.pdf')🌐 API Calls
HTTP Methods
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:
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
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
interface LoginResponse {
message: string
home_page: string
user: string
}
const response = await auth.loginWithUsernamePassword<LoginResponse>({
username: 'admin',
password: 'password',
})📖 Examples
Complete Application Example
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
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:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
# 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 file for details.
🆘 Support
- 📧 Issues: GitHub Issues
- 📖 Documentation: GitHub Wiki
- 💬 Discussions: GitHub Discussions
Made with ❤️ by the DHia A. SHalabi
