IRIS App SDK Reference
Overview
The Magentrix SDK (@magentrix-corp/magentrix-sdk) is the primary way IRIS Apps read and write data. It wraps the REST API V3 and handles authentication, session management, token refresh, and response parsing automatically. For the vast majority of data operations, you will never need to call the REST API directly.
The SDK supports both ESM and CommonJS module formats and ships with full TypeScript type definitions.
Installation
npm install @magentrix-corp/magentrix-sdk
Configuration
The SDK is initialised through the MagentrixConfig interface and a MagentrixClient instance.
import { MagentrixClient, type MagentrixConfig } from '@magentrix-corp/magentrix-sdk'
const config: MagentrixConfig = {
baseUrl: import.meta.env.VITE_SITE_URL,
refreshToken: import.meta.env.VITE_REFRESH_TOKEN,
isDevMode: import.meta.env.DEV
}
const dataService = new MagentrixClient(config)
| Property | Type | Required | Description |
|---|
baseUrl | string | Yes | Base URL of your Magentrix instance (e.g. https://yourcompany.magentrix.com). |
isDevMode | boolean | No | Enables token-based authentication. Default: false. Set to true for local development. |
refreshToken | string | No | API key used as the refresh token. Required when isDevMode is true. |
💡 Best practice: Always read credentials from environment variables (import.meta.env) rather than hardcoding them. Never commit VITE_REFRESH_TOKEN to version control.
Authentication Modes
The SDK operates in one of two authentication modes, controlled by the isDevMode flag.
Development Mode (isDevMode: true)
Uses Bearer token authentication. The SDK automatically refreshes the access token 30 seconds before it expires and validates the session on every API call. A refreshToken is required in the configuration. Use this mode for local development.
const config: MagentrixConfig = {
baseUrl: 'https://yourcompany.magentrix.com',
refreshToken: '<your-api-key>',
isDevMode: true
}
Production Mode (isDevMode: false)
Uses cookie-based authentication via ASP.NET session cookies. No Bearer tokens are sent. When the session expires, the SDK automatically redirects the user to the login page. This is the default mode and requires no refreshToken. Use this mode for apps deployed inside the Magentrix portal.
const config: MagentrixConfig = {
baseUrl: 'https://yourcompany.magentrix.com',
isDevMode: false
}
Vue 3 Composable
For Vue 3 components, use the useMagentrixSdk composable instead of instantiating MagentrixClient directly. The composable provides a singleton instance, so the same session is shared across all components.
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { MagentrixError, type MagentrixConfig } from '@magentrix-corp/magentrix-sdk'
import { useMagentrixSdk } from '@magentrix-corp/magentrix-sdk/vue'
const config: MagentrixConfig = {
baseUrl: import.meta.env.VITE_SITE_URL,
refreshToken: import.meta.env.VITE_REFRESH_TOKEN,
isDevMode: import.meta.env.DEV
}
const dataService = useMagentrixSdk().getInstance(config)
const accounts = ref<any[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function loadAccounts(): Promise<void> {
loading.value = true
error.value = null
try {
const result = await dataService.query('SELECT Id, Name FROM Account')
accounts.value = result.data
} catch (err) {
if (err instanceof MagentrixError) {
error.value = err.message
}
} finally {
loading.value = false
}
}
onMounted(() => {
loadAccounts()
})
</script>
<template>
<div>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<ul v-else>
<li v-for="account in accounts" :key="account.Id">
{{ account.Name }}
</li>
</ul>
</div>
</template>
⚠️ Warning:useMagentrixSdk() is a Vue composable. It must be called synchronously during component setup — never inside an async function, a lifecycle hook body, or a conditional block.
⚠️ Warning: Import RequestMethod from @magentrix-corp/magentrix-sdk (the main package), not from @magentrix-corp/magentrix-sdk/vue. Importing it from the Vue subpath will cause a runtime error.
API Method Reference
| Method & Signature | Return Type | Description |
|---|
create(entityName: string, data: any | any[]) | Promise<any> | Creates one or more records. Pass an array for batch operations. |
createSession(refreshToken?: string) | Promise<SessionInfo> | Creates a new session manually. Handled automatically in normal usage. |
delete(entityName: string, id: string, permanent?: boolean) | Promise<any> | Deletes a record by ID. Soft delete by default; pass true for permanent. |
deleteMany(entityName: string, ids: string[], permanent?: boolean) | Promise<any> | Deletes multiple records by ID in a single API call. |
edit(entityName: string, data: any | any[]) | Promise<any> | Updates one or more records. Each object must include an Id field. |
execute(path: string, model?: any, method?: RequestMethod) | Promise<any> | Calls a custom controller action. Use only when SDK CRUD methods are insufficient. |
getUserInfo() | Promise<UserInfo> | Returns the currently authenticated user's profile and role information. |
query(query: string) | Promise<any> | Executes a MEQL query string. Returns { data: [...] } for standard queries. |
retrieve(id: string) | Promise<any> | Retrieves a single record by ID. Returns { record: { ... } }. |
upsert(entityName: string, data: any | any[]) | Promise<any> | Creates if no Id provided, updates if Id exists. |
Method Details
query(query: string)
Executes a MEQL query and returns a data property. For standard queries, data is an array of record objects. For aggregate queries, data is an object with the aggregate field names as properties.
💡
Note: For full MEQL syntax including operators, date functions, aggregates, and pagination, see the
MEQL Reference.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'
// Standard query — returns array of records
try {
const result = await dataService.query(
'SELECT Id, Name, Email FROM Contact WHERE IsActive = true'
)
const contacts = result.data
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Query failed:', error.message)
}
}
// Aggregate query — returns object with aggregate values
try {
const result = await dataService.query(
'SELECT COUNT(Id) as Total, SUM(AnnualRevenue) as Revenue FROM Account'
)
console.log('Total:', result.data.Total)
console.log('Revenue:', result.data.Revenue)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Query failed:', error.message)
}
}
// Sorted and paginated
try {
const result = await dataService.query(
'SELECT Id, Name FROM Account ORDER BY Name ASC LIMIT 25, 0'
)
const accounts = result.data
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Query failed:', error.message)
}
}
retrieve(id: string)
Retrieves a single record by its ID. Returns an object with a record property containing the full record data with all fields accessible to the current user.
⚠️ Warning: The return shape is { record: { ... } } — not the record itself. Always access fields via result.record.FieldName, not result.FieldName.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'
try {
const result = await dataService.retrieve('00I00000000003x0001')
const account = result.record
console.log('Name:', account.Name)
console.log('Status:', account.Status)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Retrieve failed:', error.message)
}
}
// Destructured form
try {
const { record } = await dataService.retrieve('00300000000001A0001')
console.log('Contact:', record.Name)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Retrieve failed:', error.message)
}
}
create(entityName: string, data: any | any[])
Creates one or more records. Pass a single object for a single record, or an array for batch creation. Batch creation executes as a single optimised API call.
import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'
// Single record
try {
const newAccount = await dataService.create('Account', {
Name: 'Acme Corporation',
Email: 'contact@acme.com',
Status: 'Active'
})
console.log('Created ID:', newAccount.Id)
} catch (error) {
if (error instanceof DatabaseError) {
error.getErrors().forEach(err => {
console.error(`${err.fieldName}: ${err.message}`)
})
} else if (error instanceof MagentrixError) {
console.error('Create failed:', error.message)
}
}
// Batch creation — single API call
try {
const newAccounts = await dataService.create('Account', [
{ Name: 'Account 1', Status: 'Active' },
{ Name: 'Account 2', Status: 'Active' },
{ Name: 'Account 3', Status: 'Active' }
])
console.log('Created IDs:', newAccounts.map(a => a.Id))
} catch (error) {
if (error instanceof DatabaseError) {
error.getErrors().forEach(err => {
console.error(`${err.fieldName}: ${err.message}`)
})
} else if (error instanceof MagentrixError) {
console.error('Batch create failed:', error.message)
}
}
⚠️ Warning: Never use Promise.all with individual create() calls for multiple records. This fires one API request per record. Always pass an array to create() for a single optimised batch call.
edit(entityName: string, data: any | any[])
Updates one or more existing records. Every object in the payload must include an Id field. Only the fields provided are updated — omitted fields are left unchanged.
import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'
// Single record
try {
await dataService.edit('Account', {
Id: '00I00000000003x0001',
Name: 'Updated Name',
Status: 'Inactive'
})
} catch (error) {
if (error instanceof DatabaseError) {
error.getErrors().forEach(err => {
console.error(`${err.fieldName}: ${err.message}`)
})
} else if (error instanceof MagentrixError) {
console.error('Edit failed:', error.message)
}
}
// Batch update — single API call
try {
await dataService.edit('Account', [
{ Id: '00I00000000003x0001', Status: 'Active' },
{ Id: '00I00000000003y0001', Status: 'Inactive' }
])
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Batch edit failed:', error.message)
}
}
upsert(entityName: string, data: any | any[])
Creates or updates records. If an Id is provided and the record exists, it is updated. If no Id is provided, a new record is created. Supports batch operations.
import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'
try {
const records = await dataService.upsert('Contact', [
{ Id: '00300000000001A0001', Name: 'John Doe', Email: 'john@example.com' },
{ Name: 'Jane Smith', Email: 'jane@example.com' }
])
console.log('Upserted:', records.length, 'records')
} catch (error) {
if (error instanceof DatabaseError) {
error.getErrors().forEach(err => {
console.error(`${err.fieldName}: ${err.message}`)
})
} else if (error instanceof MagentrixError) {
console.error('Upsert failed:', error.message)
}
}
delete(entityName: string, id: string, permanent?: boolean)
Deletes a record by ID. Performs a soft delete by default — the record is deactivated but can be restored. Pass true as the third argument for permanent deletion.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'
// Soft delete (default)
try {
await dataService.delete('Account', '00I00000000003x0001')
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Delete failed:', error.message)
}
}
// Permanent delete
try {
await dataService.delete('Account', '00I00000000003x0001', true)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Delete failed:', error.message)
}
}
deleteMany(entityName: string, ids: string[], permanent?: boolean)
Deletes multiple records by ID in a single API call. Supports soft and permanent deletion.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'
try {
await dataService.deleteMany('Contact', [
'00300000000001A0001',
'00300000000001B0001',
'00300000000001C0001'
])
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Delete many failed:', error.message)
}
}
getUserInfo()
Returns profile and role information about the currently authenticated user as a UserInfo object.
import { MagentrixError, type UserInfo } from '@magentrix-corp/magentrix-sdk'
try {
const userInfo: UserInfo = await dataService.getUserInfo()
console.log('Name:', userInfo.name)
console.log('Role:', userInfo.roleType)
console.log('Locale:', userInfo.locale)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Failed to get user info:', error.message)
}
}
UserInfo Properties:
| Property | Type | Description |
|---|
id | string | The user's unique ID. |
name | string | Full display name. |
userName | string | Login username. |
roleType | string | Role type: 'guest', 'employee', 'customer', 'partner', or 'admin'. |
lang | string | Language code. |
locale | string | Locale setting. |
user_timezone | number | Timezone offset. |
preferred_currency | string | Preferred currency ISO code. |
userCurrencySymbol | string | Currency symbol. |
display_user_currency | boolean | Whether to display amounts in the user's currency. |
guest | boolean | Whether the user is a guest. |
impr | boolean | Whether the user is using delegated access (impersonation). |
execute(path: string, model?: any, method?: RequestMethod)
Calls a custom controller action. Defaults to POST if no method is specified. Use this method only when the SDK's standard CRUD methods cannot fulfil the requirement.
import { MagentrixError, RequestMethod } from '@magentrix-corp/magentrix-sdk'
// POST request (default)
try {
const result = await dataService.execute(
'/acls/OrderController/ProcessOrder',
{ accountId: '00I00000000003x0001', amount: 500 }
)
console.log('Result:', result)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Request failed:', error.message)
}
}
// GET request — no body permitted
try {
const data = await dataService.execute(
'/acls/ReportController/GetSummary',
null,
RequestMethod.get
)
console.log('Summary:', data)
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Request failed:', error.message)
}
}
⚠️ Warning: GET requests must not include a request body. The SDK throws a MagentrixError if a body is passed with RequestMethod.get.
createSession(refreshToken?: string)
Creates a new session manually. In normal usage you do not need to call this — the SDK creates and refreshes sessions automatically. Use it only when you require explicit control over session timing.
import { MagentrixError } from '@magentrix-corp/magentrix-sdk'
try {
const session = await dataService.createSession()
console.log('Token:', session.token)
console.log('Expires in:', session.expires_in, 'seconds')
} catch (error) {
if (error instanceof MagentrixError) {
console.error('Session creation failed:', error.message)
}
}
Error Handling
The SDK provides two error classes. Always check for DatabaseError first since it extends MagentrixError.
MagentrixError — General SDK errors covering authentication failures, network issues, and API-level errors.
DatabaseError — Extends MagentrixError. Thrown on validation failures during create, edit, or upsert. Provides hasErrors() and getErrors() to inspect individual field-level errors.
import { MagentrixError, DatabaseError } from '@magentrix-corp/magentrix-sdk'
try {
await dataService.create('Account', {
Name: 'Acme Corporation',
Email: 'contact@acme.com'
})
} catch (error) {
if (error instanceof DatabaseError) {
// Field-level validation errors
error.getErrors().forEach(err => {
console.error(`${err.fieldName}: ${err.message} (Code: ${err.code})`)
})
} else if (error instanceof MagentrixError) {
// General API or auth error
console.error('API Error:', error.message)
} else {
console.error('Unexpected error:', error)
}
}
Common Error Codes
| HTTP Status | Error Code | Description |
|---|
| 400 | PAYLOAD_MISSING | Request body is empty. A valid JSON payload is required. |
| 400 | ENTITY_ID_MISMATCH | The ID in the request body does not match the record ID in the URL. |
| 401 | — | Session expired. Re-authenticate. |
| 403 | 403 | Invalid token or too many failed authentication attempts. |
| 404 | INVALID_ENTITY | The specified entity does not exist. |
| 404 | MISSING_ENTITY | Record no longer exists. |
| 404 | ENTITY_NOT_FOUND | Record was not found or the current user lacks access. |
| 406 | Missing_Field | A required field was not provided. |
| 413 | PAYLOAD_TOO_LARGE | The payload exceeds the 20 MB size limit. |
| 500 | — | An unexpected server error occurred. |
IrisDataResponse — Server Response Contract
When calling query(), retrieve(), or execute() targeting a controller that returns IrisData(), the server returns a structured envelope called IrisDataResponse. Understanding this structure is essential for consuming entity data and building dynamic UIs.
Response Structure
Every IrisDataResponse can contain a mix of entity data — enriched with metadata, permissions, and field-level security — and plain data — serialised as-is with no enrichment.
{
"records": [ { "Id": "001...", "Name": "Acme Corp", "Status": "Active" } ],
"totalCount": 42,
"pageTitle": "Accounts",
"__entityTypes": {
"records": "Account"
},
"__permissions": {
"records": {
"create": true,
"items": [
{ "id": "001...", "update": true, "delete": false }
]
}
},
"__metadatas": {
"Account": {
"name": "Account",
"label": "Account",
"fields": [ ... ],
"recordTypes": [ ... ]
}
}
}
In this example, records is entity data (listed in __entityTypes), while totalCount and pageTitle are plain values with no metadata or permission enrichment.
Entity Types Map (__entityTypes)
Maps each response property name to its entity type. Properties not listed here are plain data.
const response = await dataService.execute('/acls/AccountController/GetDashboard')
if (response.__entityTypes) {
for (const [propertyName, entityName] of Object.entries(response.__entityTypes)) {
console.log(`${propertyName} contains ${entityName} records`)
}
}
Permissions (__permissions)
Contains per-record CRUD permissions for entity properties. Permissions are only present when the controller explicitly requests them using the With flags enum (Create = 1, Update = 2, Delete = 4).
// Check entity-level create permission
const canCreate = response.__permissions?.records?.create ?? false
// Check per-record update and delete permissions
response.__permissions?.records?.items?.forEach(item => {
console.log(`Record ${item.id}: update=${item.update}, delete=${item.delete}`)
})
Metadata (__metadatas)
Contains full entity metadata for every entity type in the response. When records include populated lookup fields, the metadata for the referenced entity types is also included automatically — so the UI can render lookup details without additional API calls.
const accountMeta = response.__metadatas?.Account
if (accountMeta) {
console.log('Label:', accountMeta.label)
console.log('Fields:', accountMeta.fields.length)
}
Entity Metadata Properties
| Property | Type | Description |
|---|
fields | EntityFieldMetadata[] | Full list of field definitions for this entity. |
id | string | Unique entity ID. |
isCustom | boolean | Whether this is a custom entity. |
isPermissionable | boolean | Whether role-based permissions apply. |
keyPrefix | string | ID prefix for records of this entity type. |
label | string | Display label (localised if multi-language is enabled). |
name | string | API name of the entity. |
pluralLabel | string | Plural display label. |
recordTypes | RecordTypeMetadata[] | Record type definitions with picklist restrictions. |
relationships | EntityRelationshipMetadata[] | Relationship definitions for reference fields. |
Field Metadata Properties
Each field in the fields array provides comprehensive information for building dynamic forms and list views.
| Property | Type | Description |
|---|
fieldType | string | Data type: ID, Text, Number, Currency, Percent, Date, DateTime, Email, Phone, Url, Picklist, PicklistMultiSelect, Lookup, MasterDetail, RichText, CheckBox, and others. |
id | string | Unique field ID. |
isCalculated | boolean | Whether the field is a formula or calculated field. |
isCustomField | boolean | Whether this is a custom field. |
isFilterable | boolean | Whether the field supports filtering. |
isReadable | boolean | Whether the current user can read this field. If false, the field value is stripped from the response before it reaches the client. |
isReadOnly | boolean | Whether the field is read-only. |
isReference | boolean | Whether this is a lookup or master-detail field. |
isRequired | boolean | Whether the field is required on create or edit. |
isSortable | boolean | Whether the field supports sorting. |
label | string | Localised display label. |
length | number | Maximum string length. |
name | string | API name of the field. |
picklistEntries | PicklistEntry[] | Available options for picklist fields. |
referenceTo | string | Target entity name for lookup and master-detail fields. |
referenceType | string | Reference type: None, MasterDetail, or Lookup. |
💡 Note: Field-level security is enforced at the server serialisation layer. If a user's role does not have read access to a field, that field is omitted from the response regardless of what the controller returns.
TypeScript Exports
The SDK exports the following types and classes for full type safety:
import {
MagentrixClient,
MagentrixConfig,
SessionInfo,
UserInfo,
MagentrixError,
DatabaseError,
RequestMethod,
ContentType
} from '@magentrix-corp/magentrix-sdk'
What to Read Next