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¢er_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¢er_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:
- User JWT must have grant for this security area
- Grant level must be ≥ required level in TB_MENU
- Source must exist in TB_MENU for dimension
Documentation: Nested Set Model
center_dett (Recommended)
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¢er_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:
| Operator | Meaning | Example |
|---|---|---|
eq | Equal | XPRD02 eq 49.99 |
ne | Not equal | XPRD06 ne false |
gt | Greater than | XPRD02 gt 50 |
lt | Less than | XPRD02 lt 100 |
ge | Greater or equal | XPRD02 ge 49.99 |
le | Less or equal | XPRD02 le 99.99 |
AND | Logical AND | XPRD02 gt 50 AND XPRD06 eq true |
OR | Logical OR | XPRD05 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¢er_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 areacenter_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¢er_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"
}
]
Example 2: Paginated Search
Scenario: Search products by name with pagination
# Page 1
curl -X GET "https://coreservice.q01.io/api/v4/core/PRD?source=productSearch¢er_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¢er_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¢er_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:
- ✅ Flexible querying - Filter, sort, paginate with query parameters
- ✅ Field control - Select only needed fields with $select
- ✅ Automatic filtering - TREC and ambiente filtering by default
- ✅ Permission enforcement - Multi-level validation on every request
- ✅ Transparent routing - CoreService → CoreQuery (you don't see it)
- ✅ 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.