Skip to main content

Read Operations (GET)

Overview

Read operations retrieve data from dimensions using GET requests to CoreService. Requests are transparently routed to CoreQuery backend for optimized read performance.

Key Principle:

GET requests to CoreService automatically route to CoreQuery. You never call CoreQuery directly - CoreService handles all routing transparently.

Basic Endpoint

GET /api/v4/core/{DIM}?source={SECURITY_AREA}

Components:

  • {DIM}: Dimension code from TB_DIM (e.g., PRD, ORD, CLI)
  • source: Required security area for permission validation

Example:

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList" \
-H "Authorization: Bearer {JWT_TOKEN}"

Two Read Modes

1. List Mode - Get Multiple Records

Endpoint:

GET /api/v4/core/{DIM}?source={SECURITY_AREA}

Returns: Array of records

Example Request:

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&center_dett=visualizza"

Example Response:

[
{
"PRD_ID": "1",
"XPRD01": "Premium Widget",
"XPRD02": 49.99,
"XPRD06": true
},
{
"PRD_ID": "2",
"XPRD01": "Standard Widget",
"XPRD02": 29.99,
"XPRD06": true
}
]

2. Detail Mode - Get Single Record by ID

Endpoint:

GET /api/v4/core/{DIM}/{ID}?source={SECURITY_AREA}

Returns: Single object (not array)

Example Request:

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD/123?source=productDetail&center_dett=visualizza"

Example Response:

{
"PRD_ID": "123",
"XPRD01": "Premium Widget",
"XPRD02": 49.99,
"XPRD03": "WDG-2025-001",
"XPRD06": true,
"XPRD05": "Electronics",
"OWNER": "user_5",
"CDATA": "20250115103045",
"LOWNER": "user_12",
"LDATA": "20250118141522",
"TREC": "M"
}

Required Parameters

source (Required)

Purpose: Security area for permission validation via TB_MENU

Example:

?source=productList

Validation:

  1. User JWT must have grant for this security area
  2. Grant level must be ≥ required level in TB_MENU
  3. Source must exist in TB_MENU for dimension

Documentation: Nested Set Model

Purpose: Context for field visibility filtering via COD_ON_OFF

Common Values:

  • visualizza - View/list context (shows L,D fields)
  • nuovo - Create form context (shows N fields)
  • modifica - Edit form context (shows M fields)
  • ricerca - Search context (shows R,S fields)

Example:

?source=productList&center_dett=visualizza

Effect: Only fields with COD_ON_OFF containing 'L' or 'D' are returned

Documentation: Field Visibility

Query Parameters

$filter - Filtering Records

Purpose: Filter results based on field values using operators

Syntax:

?$filter={FIELD} {OPERATOR} {VALUE}

Operators:

OperatorMeaningExample
eqEqualXPRD02 eq 49.99
neNot equalXPRD06 ne false
gtGreater thanXPRD02 gt 50
ltLess thanXPRD02 lt 100
geGreater or equalXPRD02 ge 49.99
leLess or equalXPRD02 le 99.99
ANDLogical ANDXPRD02 gt 50 AND XPRD06 eq true
ORLogical ORXPRD05 eq 'Electronics' OR XPRD05 eq 'Gadgets'

Examples:

Filter by exact value:

GET /api/v4/core/PRD?source=productList&$filter=XPRD01 eq 'Premium Widget'

Filter by range:

GET /api/v4/core/PRD?source=productList&$filter=XPRD02 gt 50 AND XPRD02 lt 100

Filter by boolean:

GET /api/v4/core/PRD?source=productList&$filter=XPRD06 eq true

Complex filter with OR:

GET /api/v4/core/PRD?source=productList&$filter=XPRD05 eq 'Electronics' OR XPRD05 eq 'Gadgets'

Filter by TREC state:

# Only active records (exclude cancelled)
GET /api/v4/core/PRD?source=productList&$filter=TREC ne 'C'

# Only cancelled records
GET /api/v4/core/PRD?source=productList&cond_trec=C

# Include all states
GET /api/v4/core/PRD?source=productList&cond_trec=N,M,C,P

Important: Use cond_trec parameter for TREC filtering (not $filter)

$select - Field Projection

Purpose: Return only specific fields (reduce payload size)

Syntax:

?$select={FIELD1},{FIELD2},{FIELD3}

Example:

# Return only ID, name, and price
GET /api/v4/core/PRD?source=productList&$select=PRD_ID,XPRD01,XPRD02

Response:

[
{
"PRD_ID": "1",
"XPRD01": "Premium Widget",
"XPRD02": 49.99
},
{
"PRD_ID": "2",
"XPRD01": "Standard Widget",
"XPRD02": 29.99
}
]

Use Cases:

  • Mobile apps - reduce bandwidth
  • List views - only show necessary columns
  • Performance - less data transfer

$order - Sorting

Purpose: Sort results by one or more fields

Syntax:

?$order={FIELD1} {ASC|DESC},{FIELD2} {ASC|DESC}

Examples:

Sort by single field (ascending):

GET /api/v4/core/PRD?source=productList&$order=XPRD01 ASC

Sort by single field (descending):

GET /api/v4/core/PRD?source=productList&$order=XPRD02 DESC

Multi-field sort:

# Sort by category (ASC), then price (DESC)
GET /api/v4/core/PRD?source=productList&$order=XPRD05 ASC,XPRD02 DESC

Default: If not specified, typically ordered by primary key ASC

$num_rows - Limit Results

Purpose: Return maximum N records

Syntax:

?$num_rows={NUMBER}

Example:

# Get top 10 products
GET /api/v4/core/PRD?source=productList&$num_rows=10

Use Cases:

  • Pagination
  • "Top N" lists
  • Performance optimization

$offset - Offset Results

Purpose: Skip first N records (pagination)

Syntax:

?$offset={NUMBER}

Example:

# Skip first 20 records
GET /api/v4/core/PRD?source=productList&$offset=20

Pagination Pattern

Combine $num_rows and $offset for pagination:

# Page 1 (records 1-10)
GET /api/v4/core/PRD?source=productList&$num_rows=10&$offset=0

# Page 2 (records 11-20)
GET /api/v4/core/PRD?source=productList&$num_rows=10&$offset=10

# Page 3 (records 21-30)
GET /api/v4/core/PRD?source=productList&$num_rows=10&$offset=20

Formula:

$offset = (page - 1) * pageSize
$num_rows = pageSize

JavaScript Example:

function fetchPage(page, pageSize) {
const skip = (page - 1) * pageSize;
return fetch(
`/api/v4/core/PRD?source=productList&$num_rows=${pageSize}&$offset=${skip}`
);
}

// Get page 3 with 25 items per page
fetchPage(3, 25); // $offset=50, $num_rows=25

Combining Parameters

All query parameters can be combined:

GET /api/v4/core/PRD?source=productList&center_dett=visualizza&$filter=XPRD02 gt 50 AND XPRD06 eq true&$select=PRD_ID,XPRD01,XPRD02&$order=XPRD02 DESC&$num_rows=10&$offset=0

Breakdown:

  • source=productList - Security area
  • center_dett=visualizza - Field visibility context
  • $filter=... - Only products with price > 50 and active
  • $select=... - Return only ID, name, price
  • $order=XPRD02 DESC - Sort by price descending
  • $num_rows=10 - Limit to 10 results
  • $offset=0 - Start from first record (page 1)

Default Filtering

TREC Filtering

By default, CoreQuery excludes TREC='C' (cancelled) records:

GET /api/v4/core/PRD?source=productList

Equivalent to:

SELECT * FROM TB_ANAG_PRD00 WHERE TREC != 'C'

To include cancelled records:

GET /api/v4/core/PRD?source=productList&cond_trec=N,M,C,P

Ambiente Filtering

Automatically filtered by tenant (ambiente) from JWT:

-- Added automatically based on JWT
WHERE XPRD01_AMBIENTE = 'tenant_123'

You cannot query other tenants' data - enforced by CoreQuery.

Documentation: Context Chain

Practical Examples

Example 1: Product List for Display

Scenario: Show active products in list view

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&center_dett=visualizza&$filter=XPRD06 eq true&$select=PRD_ID,XPRD01,XPRD02,XPRD05&$order=XPRD01 ASC" \
-H "Authorization: Bearer eyJhbGc..."

Response:

[
{
"PRD_ID": "1",
"XPRD01": "Premium Widget",
"XPRD02": 49.99,
"XPRD05": "Electronics"
},
{
"PRD_ID": "2",
"XPRD01": "Standard Widget",
"XPRD02": 29.99,
"XPRD05": "Electronics"
}
]

Scenario: Search products by name with pagination

# Page 1
curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productSearch&center_dett=ricerca&$filter=contains(XPRD01,'Widget')&$num_rows=25&$offset=0&$order=XPRD01 ASC" \
-H "Authorization: Bearer eyJhbGc..."

JavaScript Implementation:

class ProductSearch {
constructor(apiBase, token) {
this.apiBase = apiBase;
this.token = token;
this.pageSize = 25;
}

async search(query, page = 1) {
const skip = (page - 1) * this.pageSize;
const url = new URL(`${this.apiBase}/api/v4/core/PRD`);

url.searchParams.append('source', 'productSearch');
url.searchParams.append('center_dett', 'ricerca');
url.searchParams.append('$filter', `contains(XPRD01,'${query}')`);
url.searchParams.append('$num_rows', this.pageSize);
url.searchParams.append('$offset', skip);
url.searchParams.append('$order', 'XPRD01 ASC');

const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${this.token}`
}
});

return response.json();
}
}

// Usage
const search = new ProductSearch('https://coreservice.q01.io', token);
const results = await search.search('Widget', 2); // Page 2

Example 3: Product Detail

Scenario: Get full product details for editing

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD/123?source=productDetail&center_dett=modifica" \
-H "Authorization: Bearer eyJhbGc..."

Response:

{
"PRD_ID": "123",
"XPRD01": "Premium Widget",
"XPRD02": 49.99,
"XPRD03": "WDG-2025-001",
"XPRD06": true,
"XPRD05": "Electronics",
"XPRD04": "High-quality widget with premium features",
"XPRD09": 150,
"OWNER": "user_5",
"CDATA": "20250115103045",
"LOWNER": "user_12",
"LDATA": "20250118141522",
"TREC": "M"
}

Note: center_dett=modifica returns fields with COD_ON_OFF containing 'M'

Example 4: Dashboard Summary

Scenario: Get key metrics for dashboard

# Total active products
curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&$filter=XPRD06 eq true&$select=PRD_ID" \
-H "Authorization: Bearer eyJhbGc..."

# Top 5 most expensive products
curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&$select=PRD_ID,XPRD01,XPRD02&$order=XPRD02 DESC&$num_rows=5" \
-H "Authorization: Bearer eyJhbGc..."

# Products by category
curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&$filter=XPRD05 eq 'Electronics'&$select=PRD_ID" \
-H "Authorization: Bearer eyJhbGc..."

Example 5: Audit Trail Query

Scenario: Find recently modified records

curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productList&$filter=LDATA gt 20250101000000&$select=PRD_ID,XPRD01,LOWNER,LDATA&$order=LDATA DESC" \
-H "Authorization: Bearer eyJhbGc..."

Response:

[
{
"PRD_ID": "123",
"XPRD01": "Premium Widget",
"LOWNER": "user_12",
"LDATA": "20250119141522"
},
{
"PRD_ID": "456",
"XPRD01": "Standard Widget",
"LOWNER": "user_8",
"LDATA": "20250118103045"
}
]

Response Headers

Useful headers in response:

HTTP/1.1 200 OK
Content-Type: application/json
X-Total-Count: 250 # Total records matching filter (if available)
X-Page-Size: 25 # Requested page size
X-Page-Number: 2 # Current page number

Note: Header availability depends on CoreQuery configuration.

Error Responses

400 Bad Request - Invalid Filter

GET /api/v4/core/PRD?source=productList&$filter=INVALID_FIELD eq 'test'

Response:

{
"code": 400,
"message": "Invalid filter: field INVALID_FIELD does not exist",
"errors": [
{
"field": "$filter",
"message": "Field INVALID_FIELD not found in TB_COST for dimension PRD"
}
]
}

401 Unauthorized - Missing JWT

GET /api/v4/core/PRD?source=productList
# Missing Authorization header

Response:

{
"code": 401,
"message": "Unauthorized: JWT token required"
}

403 Forbidden - Insufficient Permissions

GET /api/v4/core/PRD?source=productAdmin
# User doesn't have grant for productAdmin

Response:

{
"code": 403,
"message": "Forbidden: insufficient permissions for source 'productAdmin'",
"errors": [
{
"field": "source",
"message": "User grant level (1) is less than required (3)"
}
]
}

404 Not Found - Record Doesn't Exist

GET /api/v4/core/PRD/99999?source=productDetail

Response:

{
"code": 404,
"message": "Record not found",
"errors": [
{
"field": "PRD_ID",
"message": "No record found with ID 99999"
}
]
}

Best Practices

✅ DO: Use Field Projection for Lists

Reduce payload size:

# Good - only necessary fields
GET /api/v4/core/PRD?source=productList&$select=PRD_ID,XPRD01,XPRD02

# Avoid - returns all fields (slow for large datasets)
GET /api/v4/core/PRD?source=productList

✅ DO: Always Specify center_dett

Ensure correct field visibility:

# Good - explicit context
GET /api/v4/core/PRD?source=productList&center_dett=visualizza

# Avoid - relies on defaults
GET /api/v4/core/PRD?source=productList

✅ DO: Use Pagination for Large Datasets

Prevent timeout and memory issues:

# Good - paginated
GET /api/v4/core/PRD?source=productList&$num_rows=50&$offset=0

# Avoid - all records at once (10k+ records)
GET /api/v4/core/PRD?source=productList

✅ DO: Filter on Indexed Fields

Better performance:

# Good - filter on primary key or indexed field
GET /api/v4/core/PRD?source=productList&$filter=XPRD03 eq 'WDG-2025-001'

# Slower - filter on non-indexed text field
GET /api/v4/core/PRD?source=productList&$filter=contains(XPRD04,'widget')

❌ DON'T: Use SQL Operators in $filter

Wrong:

# Wrong - SQL syntax
GET /api/v4/core/PRD?source=productList&$filter=XPRD02 > 50

# Right - use operators
GET /api/v4/core/PRD?source=productList&$filter=XPRD02 gt 50

Why: Prevents SQL injection, ensures consistent parsing.

❌ DON'T: Request Unnecessary Fields

Wrong:

# Returns 50+ fields when only need 3
GET /api/v4/core/PRD/123?source=productDetail

Right:

# Returns only needed fields
GET /api/v4/core/PRD/123?source=productDetail&$select=PRD_ID,XPRD01,XPRD02

❌ DON'T: Fetch All Records Without Pagination

Wrong:

# Tries to load 100k records
GET /api/v4/core/ORD?source=orderList

Right:

# Loads page by page
GET /api/v4/core/ORD?source=orderList&$num_rows=100&$offset=0

Performance Tips

1. Use Indexes

Filter on indexed fields (primary keys, foreign keys, frequently queried fields):

# Fast - indexed field
GET /api/v4/core/PRD?source=productList&$filter=PRD_ID eq 123

# Slower - non-indexed field
GET /api/v4/core/PRD?source=productList&$filter=contains(XPRD14,'test')

2. Limit Field Count

Request only fields you need:

# Fast - 3 fields
GET /api/v4/core/PRD?source=productList&$select=PRD_ID,XPRD01,XPRD02

# Slower - 50+ fields
GET /api/v4/core/PRD?source=productList

3. Use Appropriate Page Sizes

Balance between requests and payload:

# Too small - many requests
GET /api/v4/core/PRD?source=productList&$num_rows=5

# Optimal - reasonable page size
GET /api/v4/core/PRD?source=productList&$num_rows=50

# Too large - timeout risk
GET /api/v4/core/PRD?source=productList&$num_rows=10000

Recommended: 25-100 records per page for most use cases.

4. Cache Responses

Client-side caching for static data:

const cache = new Map();

async function getProduct(id) {
if (cache.has(id)) {
return cache.get(id);
}

const response = await fetch(`/api/v4/core/PRD/${id}?source=productDetail`);
const data = await response.json();

cache.set(id, data);
return data;
}

Summary

Read Operations provide:

  1. Flexible querying - Filter, sort, paginate with query parameters
  2. Field control - Select only needed fields with $select
  3. Automatic filtering - TREC and ambiente filtering by default
  4. Permission enforcement - Multi-level validation on every request
  5. Transparent routing - CoreService → CoreQuery (you don't see it)
  6. Consistent responses - Arrays for lists, objects for single records

Key Insight:

Read operations are the foundation of data access. Master $filter, $select, $order, $num_rows, and $offset parameters to build efficient, performant queries that retrieve exactly what you need, nothing more.

Next Steps:

Continue to Write Operations → to learn how to create, update, and delete data.