docker-erpnext/NODEJS_API_CLIENT.md
Brian Tan Seng b3e485db90 ⏺ The documentation update is complete! Here's what was accomplished:
📋 Documentation Updated

  1. PROJECT_OVERVIEW.md - Complete 420-line project summary
  2. README.md - Transformed into comprehensive 450+ line guide
  3. API_GUIDE.md - Enhanced with Node.js/Axios examples
  4. API_SECURITY.md - Added Node.js security implementations
  5. CLAUDE.md - Updated with latest API client information

  🎯 Project Status

  Your ERPNext Docker deployment now provides:

  - Complete API Integration: 771 DocTypes documented
  - Dual Language Support: Python + Node.js/Axios clients
  - Enterprise Security: Token auth, rate limiting, audit logging
  - Production Ready: Comprehensive testing and validation

  🚀 Ready to Use

  # Start ERPNext
  docker network create erpnext-local
  docker-compose up -d

  # Test API clients
  python3 secure_api_client.py
  node secure_api_client.js

  All documentation is up-to-date and reflects the complete work accomplished. The project is
  production-ready with enterprise-grade API integration capabilities.
2025-08-22 17:46:29 +08:00

533 lines
14 KiB
Markdown

# ERPNext Secure API Client - Node.js/Axios Version
A production-ready, secure API client for ERPNext built with Node.js and Axios, featuring comprehensive security practices, error handling, and performance optimizations.
## 🚀 Quick Start
### Installation
```bash
# Install dependencies
npm install axios dotenv
# Copy environment template
cp .env.example .env
# Edit .env with your credentials
nano .env
```
### Basic Usage
```javascript
const { ERPNextSecureClient } = require('./secure_api_client');
async function example() {
const client = new ERPNextSecureClient('http://localhost:8080');
// Authenticate with API token (recommended)
await client.authenticateWithToken();
// Get customers
const customers = await client.get('/api/resource/Customer');
console.log(customers.data);
// Create new customer
const newCustomer = await client.post('/api/resource/Customer', {
customer_name: 'Test Customer',
customer_type: 'Individual'
});
await client.logout();
}
```
## 🔐 Authentication Methods
### 1. API Token Authentication (Recommended)
```javascript
// Using environment variables (.env file)
const client = new ERPNextSecureClient();
await client.authenticateWithToken(); // Uses ERPNEXT_API_KEY and ERPNEXT_API_SECRET
// Or pass credentials directly
await client.authenticateWithToken('your_api_key', 'your_api_secret');
```
### 2. Session Authentication (Web Apps)
```javascript
await client.loginWithCredentials('username', 'password');
```
## 🛡️ Security Features
### Built-in Security
-**HTTPS Enforcement**: Warns about HTTP usage
-**Request/Response Logging**: Audit trail for security
-**Error Handling**: Secure error messages without data leakage
-**Rate Limiting**: Prevents API abuse
-**Request Timeouts**: Prevents hanging requests
-**Input Validation**: Validates all inputs
### Authentication Security
-**Token-based Auth**: Stateless and secure
-**Session Management**: Proper session handling for web apps
-**Credential Protection**: Never logs sensitive data
-**Auto-logout**: Cleans up sessions
## 📊 Advanced Features
### Advanced Client with Caching and Retry Logic
```javascript
const { ERPNextAdvancedSecureClient } = require('./secure_api_client');
const client = new ERPNextAdvancedSecureClient('http://localhost:8080', {
retryAttempts: 3, // Retry failed requests
retryDelay: 1000, // Delay between retries (ms)
enableCache: true, // Enable response caching
cacheTimeout: 300000, // Cache timeout (5 minutes)
rateLimitPerMinute: 60 // Rate limit requests
});
await client.authenticateWithToken();
// First call hits API
await client.get('/api/resource/Customer');
// Second call uses cache
await client.get('/api/resource/Customer');
```
### Performance Features
- 🚀 **Response Caching**: Reduces API calls
- 🔄 **Automatic Retries**: Handles temporary failures
- ⏱️ **Rate Limiting**: Respects API limits
- 📈 **Request Tracking**: Performance monitoring
## 📋 Practical Examples
### Customer Management
```javascript
// Get all active customers
const customers = await client.get('/api/resource/Customer', {
filters: JSON.stringify([['disabled', '=', 0]]),
fields: JSON.stringify(['name', 'customer_name', 'customer_type']),
limit_page_length: 10
});
// Create new customer
const newCustomer = await client.post('/api/resource/Customer', {
customer_name: 'API Test Customer',
customer_type: 'Company',
customer_group: 'All Customer Groups',
territory: 'All Territories'
});
// Update customer
await client.put(`/api/resource/Customer/${newCustomer.data.name}`, {
customer_type: 'Individual'
});
```
### Item Management
```javascript
// Get items with stock info
const items = await client.get('/api/resource/Item', {
fields: JSON.stringify(['item_code', 'item_name', 'standard_rate']),
filters: JSON.stringify([['is_stock_item', '=', 1]]),
order_by: 'item_name asc'
});
// Create new item
const newItem = await client.post('/api/resource/Item', {
item_code: 'API-ITEM-001',
item_name: 'API Test Item',
item_group: 'All Item Groups',
stock_uom: 'Nos',
standard_rate: 100.00
});
```
### Sales Orders
```javascript
// Get recent sales orders
const salesOrders = await client.get('/api/resource/Sales Order', {
fields: JSON.stringify(['name', 'customer', 'status', 'grand_total']),
filters: JSON.stringify([['docstatus', '=', 1]]),
order_by: 'creation desc',
limit_page_length: 5
});
// Create sales order
const salesOrder = await client.post('/api/resource/Sales Order', {
customer: 'CUST-00001',
delivery_date: '2024-12-31',
items: [{
item_code: 'ITEM-001',
qty: 10,
rate: 100.00
}]
});
```
## 🎯 Error Handling
### Comprehensive Error Handling
```javascript
try {
const result = await client.get('/api/resource/Customer');
console.log(result.data);
} catch (error) {
if (error.response) {
// Server responded with error status
const status = error.response.status;
const message = error.response.data.message;
switch (status) {
case 401:
console.error('Authentication failed');
break;
case 403:
console.error('Access forbidden');
break;
case 404:
console.error('Resource not found');
break;
case 429:
console.error('Rate limit exceeded');
break;
default:
console.error(`API error: ${status} - ${message}`);
}
} else if (error.request) {
// Network error
console.error('Network error:', error.message);
} else {
// Other error
console.error('Error:', error.message);
}
}
```
### Built-in Error Handling
The client automatically handles:
- **401 Unauthorized**: Token expired/invalid
- **403 Forbidden**: Insufficient permissions
- **429 Rate Limited**: Automatic retry after delay
- **5xx Server Errors**: Automatic retry with backoff
- **Network Timeouts**: Configurable timeout handling
## ⚙️ Configuration Options
### Environment Variables (.env)
```bash
# Required
ERPNEXT_URL=https://your-domain.com
ERPNEXT_API_KEY=your_api_key_here
ERPNEXT_API_SECRET=your_api_secret_here
# Optional Performance Settings
RATE_LIMIT_PER_MINUTE=60
REQUEST_TIMEOUT=30000
ENABLE_CACHE=true
CACHE_TIMEOUT=300000
RETRY_ATTEMPTS=3
RETRY_DELAY=1000
```
### Client Configuration
```javascript
const client = new ERPNextAdvancedSecureClient(baseUrl, {
retryAttempts: 3, // Number of retry attempts
retryDelay: 1000, // Delay between retries (ms)
enableCache: true, // Enable response caching
cacheTimeout: 300000, // Cache expiration (ms)
rateLimitPerMinute: 60 // Requests per minute limit
});
```
## 📚 Available Scripts
```bash
# Run demo with all features
npm run demo
# Run practical examples
npm run test-api
# Simple usage example
node examples/simple_usage.js
# Advanced examples
node examples/api_examples.js
```
## 🔍 Logging and Monitoring
### Security Logging
The client automatically logs to files:
- **`api_security.log`**: Authentication events
- **`api_requests.log`**: All API requests with status codes
### Example Log Entries
```
2024-08-22T09:15:30.123Z - TOKEN_AUTH_SUCCESS - User: abcd1234...
2024-08-22T09:15:31.456Z - GET /api/resource/Customer - 200 - User: admin
2024-08-22T09:15:32.789Z - POST /api/resource/Customer - 201 - User: admin
```
### Performance Monitoring
```javascript
// Track request performance
console.time('API Request');
const result = await client.get('/api/resource/Customer');
console.timeEnd('API Request');
// Cache hit/miss tracking
const cachedResult = await client.get('/api/resource/Customer'); // Cache hit logged
```
## 🚨 Security Best Practices
### Production Checklist
- [ ] ✅ Use HTTPS URLs only
- [ ] ✅ Store API keys in environment variables
- [ ] ✅ Enable request logging for audit trails
- [ ] ✅ Implement rate limiting
- [ ] ✅ Use token authentication (not sessions)
- [ ] ✅ Set appropriate timeouts
- [ ] ✅ Handle errors gracefully
- [ ] ✅ Monitor API usage patterns
- [ ] ✅ Rotate API keys regularly
- [ ] ✅ Validate all inputs
### Development Tips
```javascript
// ✅ Good: Use environment variables
const client = new ERPNextSecureClient(process.env.ERPNEXT_URL);
await client.authenticateWithToken(); // Uses env vars
// ❌ Bad: Hardcode credentials
const client = new ERPNextSecureClient('http://localhost:8080');
await client.authenticateWithToken('hardcoded-key', 'hardcoded-secret');
// ✅ Good: Handle errors
try {
const result = await client.get('/api/resource/Customer');
return result.data;
} catch (error) {
logger.error('Customer fetch failed:', error.message);
throw new APIError('Failed to fetch customers');
}
// ❌ Bad: Ignore errors
const result = await client.get('/api/resource/Customer');
return result.data; // Will crash if API fails
```
## 📖 Integration Examples
### Express.js API
```javascript
const express = require('express');
const { ERPNextSecureClient } = require('./secure_api_client');
const app = express();
const erpClient = new ERPNextSecureClient();
app.get('/customers', async (req, res) => {
try {
await erpClient.authenticateWithToken();
const customers = await erpClient.get('/api/resource/Customer', {
limit_page_length: 10
});
res.json(customers.data);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch customers' });
} finally {
await erpClient.logout();
}
});
```
### React/Next.js Frontend
```javascript
// api/erpnext.js
import { ERPNextSecureClient } from '../lib/secure_api_client';
export async function getCustomers() {
const client = new ERPNextSecureClient(process.env.NEXT_PUBLIC_ERPNEXT_URL);
await client.authenticateWithToken(
process.env.ERPNEXT_API_KEY,
process.env.ERPNEXT_API_SECRET
);
const customers = await client.get('/api/resource/Customer');
await client.logout();
return customers.data;
}
```
### CLI Tool
```javascript
#!/usr/bin/env node
const { ERPNextSecureClient } = require('./secure_api_client');
async function cli() {
const client = new ERPNextSecureClient();
await client.authenticateWithToken();
const command = process.argv[2];
switch (command) {
case 'customers':
const customers = await client.get('/api/resource/Customer');
console.table(customers.data);
break;
case 'items':
const items = await client.get('/api/resource/Item');
console.table(items.data);
break;
default:
console.log('Available commands: customers, items');
}
await client.logout();
}
cli().catch(console.error);
```
## 🔧 Troubleshooting
### Common Issues
**Authentication Failed**
```bash
# Check API key generation
# 1. Login to ERPNext → Settings → My Settings
# 2. Scroll to "API Access" section
# 3. Generate new keys if needed
# 4. Update .env file
```
**Connection Refused**
```bash
# Make sure ERPNext is running
docker-compose ps
docker-compose up -d
```
**Rate Limit Exceeded**
```javascript
// Use advanced client with rate limiting
const client = new ERPNextAdvancedSecureClient(url, {
rateLimitPerMinute: 30 // Reduce rate limit
});
```
**Timeout Errors**
```javascript
// Increase timeout
const client = new ERPNextSecureClient(url);
client.client.defaults.timeout = 60000; // 60 seconds
```
## 📦 Dependencies
```json
{
"dependencies": {
"axios": "^1.6.0", // HTTP client
"dotenv": "^16.0.0" // Environment variables
},
"devDependencies": {
"nodemon": "^3.0.0" // Development auto-reload
}
}
```
## 🎉 Complete Example
Here's a complete working example:
```javascript
const { ERPNextAdvancedSecureClient } = require('./secure_api_client');
require('dotenv').config();
async function completeExample() {
// 1. Initialize client with advanced features
const client = new ERPNextAdvancedSecureClient(process.env.ERPNEXT_URL, {
enableCache: true,
retryAttempts: 3,
rateLimitPerMinute: 60
});
try {
// 2. Authenticate
await client.authenticateWithToken();
console.log('✅ Authenticated successfully');
// 3. Get system info (cached)
const systemInfo = await client.get('/api/resource/System Settings/System Settings');
console.log(`System: ${systemInfo.data.country} (${systemInfo.data.time_zone})`);
// 4. Create customer with error handling
try {
const customer = await client.post('/api/resource/Customer', {
customer_name: `API Customer ${Date.now()}`,
customer_type: 'Individual',
customer_group: 'All Customer Groups',
territory: 'All Territories'
});
console.log(`✅ Created customer: ${customer.data.name}`);
// 5. Update the customer
await client.put(`/api/resource/Customer/${customer.data.name}`, {
customer_type: 'Company'
});
console.log('✅ Updated customer');
} catch (error) {
console.log('⚠️ Customer operations skipped:', error.response?.data?.message);
}
// 6. Get performance metrics
console.log('📊 Performance test - making 5 rapid requests...');
const promises = Array(5).fill().map(() =>
client.get('/api/resource/Company', { limit_page_length: 1 })
);
await Promise.all(promises);
console.log('✅ All requests completed (rate limited automatically)');
} catch (error) {
console.error('❌ Error:', error.message);
} finally {
// 7. Always logout
await client.logout();
console.log('🔒 Logged out');
}
}
completeExample();
```
This Node.js/Axios client provides enterprise-grade security, performance, and reliability for ERPNext API integration!