EpicorRestNode v2.0.0 Release Notes
New Features
Advanced Response Control
Added two distinct response modes to give you full control over how API responses and errors are handled:
ResponseType: Switch between SIMPLE and FULL response modes
SIMPLE: Returns data directly, errors are returned (backward compatible with v1.x)FULL: ReturnsEpicorResponsewrapper with status, message, data, and headers. Errors are thrown
PreserveSession: Control whether CreateSession() stores the session internally
true:CreateSession()stores the session inEpiSessionfor internal use (default)false:CreateSession()returns the session but setsEpiSessionto null (useful when frontend needs session ID but backend doesnβt store it)
import { EpicorRestService, EpicorResponseType } from 'epicor-rest-node';
const service = new EpicorRestService();
service.ResponseType = EpicorResponseType.FULL; // Default in v2.0.0
service.PreserveSession = true; // Default in v2.0.0
Enhanced Error Handling
Error handling now varies by response mode for maximum flexibility:
SIMPLE Mode - Errors are returned
service.ResponseType = EpicorResponseType.SIMPLE;
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
if (EpicorError.isError(result)) {
console.log(`Error ${result.status}: ${result.message}`);
} else {
console.log('Data:', result);
}
FULL Mode - Errors are thrown
service.ResponseType = EpicorResponseType.FULL;
try {
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
console.log(`Status: ${result.status} - ${result.message}`);
console.log('Data:', result.data);
console.log('Headers:', result.headers);
} catch (error) {
if (EpicorError.isError(error)) {
console.log(`Error ${error.status}: ${error.message}`);
}
}
EpicorResponse Class
New response wrapper providing comprehensive metadata (FULL mode only):
class EpicorResponse<T> {
status: number; // HTTP status code (200, 201, etc.)
message: string; // HTTP status text or custom message
data: T; // Response data of type T
headers: any; // Response headers
// Factory methods
static fromAxios<T>(response: AxiosResponse): EpicorResponse<T>
static fromAxiosWithMessage<T>(response: AxiosResponse, message: string): EpicorResponse<T>
}
Benefits:
- Access response headers without additional parameters
- Distinguish between successful responses with different status codes
- Get consistent response structure across all API methods
- Better debugging with status messages
Session Management Enhancements
CreateSession() - New capital βSβ method with improved return type
// v2.0.0 - Returns session object or null
const session = await service.CreateSession();
if (session) {
console.log(`Session ID: ${session.SessionId}`);
console.log(`User ID: ${session.UserId}`);
// ... make API calls
await service.DestroySession();
} else {
console.log('Failed to create session');
}
Createsession() - Deprecated (lowercase βsβ)
// v1.x style - Still supported but deprecated
const success = await service.Createsession(); // Returns boolean
if (success) {
// ... make API calls
await service.DestroySession();
}
New Session Context Methods:
// Set employee context
await service.SetEmployee('EMP123');
// Set plant/site context
await service.SetPlant('MfgSys');
// Set workstation context
await service.SetWorkstation('WKST001');
// Synchronize client data
await service.SetClientData('john.doe', 'DESKTOP-ABC123', 'M/d/yyyy');
// Get session information
const sessionInfo = await service.GetSessionInfo();
// Get theme and user preferences
const themeInfo = await service.GetThemeInfo();
CallSettings Improvements
Added convenience methods for common operations:
import { CallSettings } from 'epicor-rest-node/dist/models/CallSettings';
const settings = new CallSettings('EPIC06', 'MfgSys', 'en', 'en-US', '0');
// New methods
settings.SetCompany('EPIC07'); // Switch company context
settings.SetPlant('Plant02'); // Switch plant context
EFX Staging Retry Flag
Added development-only retry mechanism for Epicor Functions:
service.EfxAttemptStagingRetry = true; // Development only!
// If function returns 404, automatically retries with staging endpoint
const result = await service.EfxPost('MyLibrary', 'MyNewFunction', params);
Important: Only enable during development to test functions that exist in staging but not yet in production.
Breaking Changes
1. Default Response Type Changed
Old Behavior (v1.x):
// Always returned data directly
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
if (EpicorError.isError(result)) {
// Handle error
} else {
console.log(result); // Direct data
}
New Behavior (v2.0.0):
// Default is FULL mode - returns EpicorResponse wrapper, throws errors
try {
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
console.log(result.data); // Data is in .data property
} catch (error) {
if (EpicorError.isError(error)) {
// Handle error
}
}
Migration: Set ResponseType = EpicorResponseType.SIMPLE for v1.x behavior:
import { EpicorResponseType } from 'epicor-rest-node/dist/models/EpicorType';
service.ResponseType = EpicorResponseType.SIMPLE;
// Now works exactly like v1.x
2. CreateSession Return Type
Old (v1.x):
const success: boolean = await service.Createsession();
if (success) {
// Session created
}
New (v2.0.0):
const session: EpicorRestSession | null = await service.CreateSession();
if (session) {
console.log(session.SessionId, session.UserId);
}
Migration: Continue using deprecated Createsession() (lowercase) for boolean return, or update to new CreateSession() (capital S).
3. Error Handling in FULL Mode
Old (v1.x):
// Errors always returned
const result = await service.BoGet('Invalid.BO', 'Method');
if (EpicorError.isError(result)) {
// Handle error
}
New (v2.0.0 FULL mode):
// Errors are thrown
try {
const result = await service.BoGet('Invalid.BO', 'Method');
} catch (error) {
if (EpicorError.isError(error)) {
// Handle error
}
}
Migration: Wrap FULL mode calls in try/catch, or use SIMPLE mode for returned errors.
Usage Examples
Session Management
Create and use a session:
import { EpicorRestService, EpicorError } from 'epicor-rest-node';
const service = new EpicorRestService();
// ... configure service properties
const session = await service.CreateSession();
if (session) {
try {
console.log(`Session: ${session.SessionId}`);
// Set context
await service.SetEmployee('EMP001');
await service.SetPlant('MfgSys');
// Make API calls
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
console.log('Customers:', result.data);
// Get session info
const info = await service.GetSessionInfo();
console.log('Current User:', info.UserID);
console.log('Current Company:', info.CompanyID);
} finally {
await service.DestroySession();
}
}
Response Type Comparison
SIMPLE Mode (v1.x compatible):
service.ResponseType = EpicorResponseType.SIMPLE;
// Returns data directly
const customers = await service.BoGet('Erp.BO.Customer', 'GetList', params);
if (EpicorError.isError(customers)) {
console.log(`Error: ${customers.message}`);
return;
}
// Direct data access
customers.value.forEach(customer => {
console.log(customer.CustID);
});
FULL Mode (v2.0.0 recommended):
service.ResponseType = EpicorResponseType.FULL;
try {
// Returns EpicorResponse wrapper
const response = await service.BoGet('Erp.BO.Customer', 'GetList', params);
console.log(`HTTP ${response.status}: ${response.message}`);
console.log('Content-Type:', response.headers['content-type']);
// Access data via .data property
response.data.value.forEach(customer => {
console.log(customer.CustID);
});
} catch (error) {
if (EpicorError.isError(error)) {
console.log(`Error ${error.status}: ${error.message}`);
}
}
PreserveSession Behavior
Preserved Session (default):
service.PreserveSession = true;
const session = await service.CreateSession();
console.log('Session created:', session.SessionId);
console.log('Stored internally:', service.EpiSession?.SessionId); // Same as session.SessionId
// Session is stored and can be reused
await service.BoGet('Erp.BO.Customer', 'GetList');
await service.BaqGet('MyBAQ');
await service.DestroySession();
Non-Preserved Session:
service.PreserveSession = false;
const session = await service.CreateSession();
console.log('Session created:', session.SessionId);
console.log('Not stored internally:', service.EpiSession); // null
// Frontend can use session.SessionId (x-session-id header)
// But backend service doesn't maintain the session internally
EFX Staging Retry (Development Only)
// Only during development!
service.EfxAttemptStagingRetry = true;
try {
// Tries production endpoint first
// If 404, automatically retries with staging endpoint
const result = await service.EfxPost('MyLibrary', 'MyNewFunction', {
param1: 'value1',
param2: 'value2'
});
console.log('Function result:', result.data);
} catch (error) {
if (EpicorError.isError(error)) {
console.log('Function failed in both production and staging');
}
}
// Production: Set to false
service.EfxAttemptStagingRetry = false; // Default
Technical Details
Response Type Modes
| Feature | SIMPLE Mode | FULL Mode |
|---|---|---|
| Return Type | T | EpicorError |
EpicorResponse<T> |
| Error Handling | Errors returned | Errors thrown |
| Error Check | if (EpicorError.isError(result)) |
try/catch |
| Data Access | Direct (result) |
Via property (result.data) |
| Headers | Via separate param | In response (result.headers) |
| Status Code | Only on error | Always available (result.status) |
| Message | Only on error | Always available (result.message) |
| Backward Compatible |
Session Lifecycle
PreserveSession = true (default):
CreateSession() β Session Created β Stored in EpiSession β Returns Session Object
β
Service maintains session internally
PreserveSession = false:
CreateSession() β Session Created β EpiSession set to null β Returns Session Object
β
Frontend can use session ID
Service doesn't store internally
Method Signatures
All API methods now support:
// BO Methods
BoGet<T>(bo: string, method: string, params?: Map<string, string>,
callContext?: CallContext | null, additionalHeaders?: any,
capturedHeaders?: CapturedResponseHeaders): Promise<T | EpicorError> // SIMPLE
BoGet<T>(...): Promise<EpicorResponse<T>> // FULL (throws on error)
// BAQ Methods
BaqGet<T>(baqId: string, params?: Map<string, string>,
callContext?: CallContext | null, additionalHeaders?: any,
capturedHeaders?: CapturedResponseHeaders): Promise<T | EpicorError> // SIMPLE
BaqGet<T>(...): Promise<EpicorResponse<T>> // FULL (throws on error)
// EFX Methods
EfxPost<T>(library: string, functionName: string, params?: any,
staging?: boolean, callContext?: CallContext | null,
additionalHeaders?: any, capturedHeaders?: CapturedResponseHeaders):
Promise<T | EpicorError> // SIMPLE
EfxPost<T>(...): Promise<EpicorResponse<T>> // FULL (throws on error)
// Session Methods
CreateSession(): Promise<EpicorRestSession | null> // NEW
Createsession(): Promise<boolean> // DEPRECATED
DestroySession(): Promise<boolean>
SetEmployee(employeeID: string): Promise<boolean>
SetPlant(plantID: string): Promise<boolean>
SetWorkstation(workstationID: string): Promise<boolean>
SetClientData(clientUserName: string, clientComputerName: string,
clientDateFormat?: string, appserver?: string,
clientTerminalID?: number): Promise<boolean>
GetSessionInfo(): Promise<any>
GetThemeInfo(): Promise<any>
Migration Guide
Quick Migration for v1.x Users
Option 1: Minimal Changes (Use SIMPLE mode)
import { EpicorRestService, EpicorResponseType } from 'epicor-rest-node';
const service = new EpicorRestService();
service.ResponseType = EpicorResponseType.SIMPLE; // Add this line
service.PreserveSession = true; // Optional, defaults to true
// All your v1.x code works as-is
const result = await service.BoGet('Erp.BO.Customer', 'GetList');
if (EpicorError.isError(result)) {
// Handle error
} else {
// Use result
}
Option 2: Adopt FULL Mode (Recommended)
import { EpicorRestService, EpicorResponseType } from 'epicor-rest-node';
const service = new EpicorRestService();
service.ResponseType = EpicorResponseType.FULL; // Default, optional
// Update error handling to try/catch
try {
const response = await service.BoGet('Erp.BO.Customer', 'GetList');
console.log(response.data); // Access via .data
} catch (error) {
if (EpicorError.isError(error)) {
console.log(`Error: ${error.message}`);
}
}
Updating Session Creation
Before (v1.x):
const created = await service.Createsession();
if (created) {
// Work with session
await service.DestroySession();
}
After (v2.0.0):
const session = await service.CreateSession();
if (session) {
console.log('Session:', session.SessionId);
// Work with session
await service.DestroySession();
}
// Or continue using deprecated method:
const created = await service.Createsession(); // Still works
Handling Response Headers
Before (v1.x):
const capturedHeaders = new CapturedResponseHeaders();
const result = await service.BoGet('Erp.BO.Customer', 'GetList',
params, null, null, capturedHeaders);
const contentType = capturedHeaders.getHeader('content-type');
After (v2.0.0 FULL mode):
// Headers included in response
const response = await service.BoGet('Erp.BO.Customer', 'GetList', params);
const contentType = response.headers['content-type'];
// Or still use CapturedResponseHeaders if needed
const capturedHeaders = new CapturedResponseHeaders();
const response = await service.BoGet('Erp.BO.Customer', 'GetList',
params, null, null, capturedHeaders);
Backward Compatibility
v2.0.0 maintains backward compatibility through SIMPLE mode:
Fully Compatible:
- All v1.x code works with
ResponseType = EpicorResponseType.SIMPLE Createsession()(lowercase) still available (deprecated)- Error handling patterns unchanged in SIMPLE mode
- All existing method signatures supported
Deprecated:
Createsession()- UseCreateSession()instead- Checking
result === truefor session creation - Use object check
New Required Imports (if using FULL mode):
import { EpicorResponse } from 'epicor-rest-node';
import { EpicorResponseType } from 'epicor-rest-node/dist/models/EpicorType';
Why Upgrade?
- Better Error Handling: Choose between returned or thrown errors based on your architecture
- Rich Response Data: Access status codes, messages, and headers without extra parameters
- Improved Session Management: Get session details, manage context easily
- Type Safety:
EpicorResponse<T>provides better TypeScript support - Modern Patterns: FULL mode uses try/catch (standard JavaScript error handling)
- Flexible Sessions: Control session lifecycle with
PreserveSession - Development Tools: EFX staging retry for easier function development
- Future Ready: Built for upcoming features and improvements
Testing
Comprehensive test suite included (32+ tests):
# Copy environment template
cp .env.example .env
# Configure your Epicor connection in .env
# EPICOR_HOST=your-server.com
# EPICOR_USERNAME=your-username
# etc.
# Run tests
npm test
Tests cover:
Response type modes (SIMPLE and FULL)
Error handling (returned and thrown)
Session management (CreateSession, SetEmployee, SetPlant, etc.)
PreserveSession behavior
CallSettings methods
BO, BAQ, and EFX operations
Type guards and factory methods