Skip to main content

Create Operations (POST)

Overview

POST operations create new records in dimensions. CoreService routes POST requests to CoreWrite, which validates, populates automatic fields, executes cascades, and publishes events.

Endpoint:

POST /api/v4/core/{dim}
Content-Type: application/json
Authorization: Bearer {token}

Behavior:

  • Sets TREC='N' (New)
  • Sets CDATA, OWNER (audit fields)
  • Executes pre-insert functions (uuid, counter, md5, etc.)
  • Validates required fields
  • Validates COD_ON_OFF='N' permissions
  • Executes INSERT_CASCADE operations
  • Publishes creation event to outbox

Basic POST Request

Simple Record Creation

Request:

POST /api/v4/core/PRD
Authorization: Bearer eyJhbGc...
Content-Type: application/json

{
"data": {
"XPRD01": "Widget Pro",
"XPRD02": 99.99,
"XPRD04": "Professional widget with advanced features",
"XPRD05": "cat_electronics",
"XPRD06": true,
"XPRD09": 100
}
}

Response:

{
"status": "success",
"data": {
"PRD_ID": 123,
"XPRD01": "Widget Pro",
"XPRD02": 99.99,
"XPRD03": "PRD-2025-001", // Auto-generated counter
"XPRD04": "Professional widget with advanced features",
"XPRD05": "cat_electronics",
"XPRD06": true,
"XPRD09": 100,
"TREC": "N", // Automatically set
"CDATA": "20251219153045", // Creation timestamp
"OWNER": "user@example.com" // Creator
}
}

Automatic Field Population

Pre-Insert Functions

TB_COST.VALUE_TEMP defines automatic field population:

UUID Generation

Metadata:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, VALUE_TEMP) VALUES
('PRD', 1, 'PRD_ID', '{uuid}');

Result:

POST /api/v4/core/PRD
{
"data": {
"XPRD01": "Product Name"
// PRD_ID auto-populated: "550e8400-e29b-41d4-a716-446655440000"
}
}

Counter Generation

Metadata:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, VALUE_TEMP) VALUES
('PRD', 3, 'XPRD03', '{counter:PRD-{YYYY}-{NNN}}');

INSERT INTO TB_COUNTER (COD_DIM, COD_VAR, COUNTER_VALUE, FORMAT_MASK) VALUES
('PRD', 'XPRD03', 0, 'PRD-{YYYY}-{NNN}');

Result:

POST /api/v4/core/PRD
{
"data": {
"XPRD01": "Product Name"
// XPRD03 auto-populated: "PRD-2025-001"
}
}

Format Tokens:

  • {YYYY} - 4-digit year (2025)
  • {YY} - 2-digit year (25)
  • {MM} - 2-digit month (12)
  • {DD} - 2-digit day (19)
  • {NNN} - Sequential number with padding (001, 002, ...)
  • {N} - Sequential number without padding (1, 2, ...)

MD5 Hash

Metadata:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, VALUE_TEMP) VALUES
('USR', 5, 'XUSR05', '{md5:XUSR02}'); -- Hash password field

Result:

POST /api/v4/core/USR
{
"data": {
"XUSR01": "john.doe",
"XUSR02": "mypassword123"
// XUSR05 auto-populated: "5f4dcc3b5aa765d61d8327deb882cf99"
}
}

Timestamp

Metadata:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, VALUE_TEMP) VALUES
('LOG', 3, 'XLOG03', '{timestamp}');

Result:

POST /api/v4/core/LOG
{
"data": {
"XLOG01": "Action performed"
// XLOG03 auto-populated: "20251219153045"
}
}

Slugify

Metadata:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, VALUE_TEMP) VALUES
('PRD', 10, 'XPRD10', '{slugify:XPRD01}');

Result:

POST /api/v4/core/PRD
{
"data": {
"XPRD01": "Widget Pro 2025!"
// XPRD10 auto-populated: "widget-pro-2025"
}
}

Required Field Validation

Validation Rules

TB_COST.REQUIRED='1' marks mandatory fields:

INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, REQUIRED) VALUES
('PRD', 1, 'XPRD01', 1), -- Name required
('PRD', 5, 'XPRD05', 1); -- Category required

Validation Errors

Missing required field:

Request:

POST /api/v4/core/PRD
{
"data": {
"XPRD02": 99.99
// Missing XPRD01 (required)
}
}

Response:

{
"error": "ValidationError",
"message": "Required field missing: XPRD01",
"code": "REQUIRED_FIELD_MISSING",
"field": "XPRD01",
"dimension": "PRD"
}

COD_ON_OFF Validation

Create Permission

TB_COST.COD_ON_OFF must include 'N' flag for POST:

-- XPRD01 can be set on creation (N flag present)
INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, COD_ON_OFF) VALUES
('PRD', 1, 'XPRD01', 'LDNMR');

-- XPRD12 cannot be set on creation (N flag missing)
INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, COD_ON_OFF) VALUES
('PRD', 12, 'XPRD12', 'LDM'); -- No 'N' flag

Validation Error:

Request:

POST /api/v4/core/PRD
{
"data": {
"XPRD01": "Product",
"XPRD12": "image.jpg" // Not allowed on creation
}
}

Response:

{
"error": "ValidationError",
"message": "Field not allowed in create operation: XPRD12",
"code": "FIELD_NOT_CREATEABLE",
"field": "XPRD12",
"dimension": "PRD"
}

INSERT_CASCADE Operations

Master-Detail Creation

TB_COST.INSERT_CASCADE triggers related record creation:

-- When order is created, also create order items
INSERT INTO TB_COST (COD_DIM, NUM_COST, COD_VAR, INSERT_CASCADE) VALUES
('ORD', 10, 'items', 'ORDITEM:XORDITEM_ORDER_ID');

Single Request Creates Multiple Records:

Request:

POST /api/v4/core/ORD
{
"data": {
"XORD01": "ORD-2025-001",
"XORD_CUSTOMER_ID": "cust_123",
"XORD03": 299.97,
"items": [
{
"XORDITEM10": "prd_1",
"XORDITEM_QTY": 2,
"XORDITEM09": 99.99
},
{
"XORDITEM10": "prd_2",
"XORDITEM_QTY": 1,
"XORDITEM09": 99.99
}
]
}
}

Result:

-- Creates 1 order record
INSERT INTO TB_ANAG_ORD00 (XORD01, XORD_CUSTOMER_ID, XORD03, TREC, CDATA, OWNER)
VALUES ('ORD-2025-001', 'cust_123', 299.97, 'N', '20251219153045', 'user@example.com');

-- Creates 2 order item records (cascade)
INSERT INTO TB_ANAG_ORDITEM00 (XORDITEM_ORDER_ID, XORDITEM10, XORDITEM_QTY, XORDITEM09, TREC, CDATA, OWNER)
VALUES (123, 'prd_1', 2, 99.99, 'N', '20251219153045', 'user@example.com');

INSERT INTO TB_ANAG_ORDITEM00 (XORDITEM_ORDER_ID, XORDITEM10, XORDITEM_QTY, XORDITEM09, TREC, CDATA, OWNER)
VALUES (123, 'prd_2', 1, 99.99, 'N', '20251219153045', 'user@example.com');

Outbox Event

Event Publication

Every POST publishes event to RabbitMQ:

Outbox Record:

{
"event_id": "evt_550e8400",
"event_type": "ProductCreated",
"aggregate_id": "123",
"aggregate_type": "PRD",
"operation": "POST",
"timestamp": "2025-12-19T15:30:45Z",
"payload": {
"PRD_ID": 123,
"XPRD01": "Widget Pro",
"XPRD02": 99.99,
"XPRD03": "PRD-2025-001",
"TREC": "N"
},
"user": "user@example.com",
"session": {
"source": "productManagement",
"centro_dett": "admin",
"microservice": "adminApp"
}
}

RabbitMQ Exchange:

Exchange: corewrite.fanout
Routing Key: product.created
Subscribers: search-indexer, cache-invalidator, analytics

JavaScript Implementation

Product Creation Service

class ProductService {
constructor(apiBase, token) {
this.apiBase = apiBase;
this.token = token;
}

/**
* Create new product
*/
async createProduct(productData) {
const response = await fetch(`${this.apiBase}/api/v4/core/PRD`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ data: productData })
});

if (!response.ok) {
const error = await response.json();
throw new ProductCreationError(error);
}

return response.json();
}

/**
* Create product with validation
*/
async createProductWithValidation(productData) {
// Client-side validation
this.validateRequired(productData, ['XPRD01', 'XPRD05']);

try {
const result = await this.createProduct(productData);
console.log('Product created:', result.data.PRD_ID);
return result;
} catch (error) {
if (error.code === 'REQUIRED_FIELD_MISSING') {
console.error('Missing required field:', error.field);
} else if (error.code === 'FIELD_NOT_CREATEABLE') {
console.error('Field not allowed on creation:', error.field);
}
throw error;
}
}

/**
* Validate required fields
*/
validateRequired(data, requiredFields) {
for (const field of requiredFields) {
if (!data[field]) {
throw new Error(`Required field missing: ${field}`);
}
}
}
}

class ProductCreationError extends Error {
constructor(errorResponse) {
super(errorResponse.message);
this.code = errorResponse.code;
this.field = errorResponse.field;
this.dimension = errorResponse.dimension;
}
}

// Usage
const productService = new ProductService(apiBase, token);

const newProduct = await productService.createProduct({
XPRD01: 'Widget Pro',
XPRD02: 99.99,
XPRD04: 'Professional widget',
XPRD05: 'cat_electronics',
XPRD06: true,
XPRD09: 100
});

console.log('Created product:', newProduct.data.PRD_ID);
console.log('Counter code:', newProduct.data.XPRD03); // "PRD-2025-001"

Master-Detail Creation

class OrderService {
constructor(apiBase, token) {
this.apiBase = apiBase;
this.token = token;
}

/**
* Create order with items (master-detail)
*/
async createOrder(orderData, items) {
const response = await fetch(`${this.apiBase}/api/v4/core/ORD`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
...orderData,
items: items // Cascade insert
}
})
});

if (!response.ok) {
throw new Error('Order creation failed');
}

return response.json();
}

/**
* Create order from cart
*/
async createOrderFromCart(customerId, cartItems) {
// Calculate total
const total = cartItems.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);

// Build order data
const orderData = {
XORD_CUSTOMER_ID: customerId,
XORD03: total
};

// Build items array
const items = cartItems.map(item => ({
XORDITEM10: item.productId,
XORDITEM_QTY: item.quantity,
XORDITEM09: item.price
}));

// Create order with items in single request
const result = await this.createOrder(orderData, items);

console.log('Order created:', result.data.ORD_ID);
console.log('Order code:', result.data.XORD01);
console.log('Items created:', items.length);

return result;
}
}

// Usage
const orderService = new OrderService(apiBase, token);

const order = await orderService.createOrderFromCart('cust_123', [
{ productId: 'prd_1', quantity: 2, price: 99.99 },
{ productId: 'prd_2', quantity: 1, price: 99.99 }
]);

Common Patterns

Pattern 1: Simple Create

Single record, no cascades:

async function createCategory(name, description) {
const response = await fetch(`${apiBase}/api/v4/core/CAT`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
XCAT01: name,
XCAT02: description
}
})
});

return response.json();
}

Pattern 2: Create with Auto-Generated Fields

Let system populate counters, UUIDs:

async function createProduct(data) {
// Don't include PRD_ID (uuid) or XPRD03 (counter)
// System will auto-generate
const response = await fetch(`${apiBase}/api/v4/core/PRD`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
XPRD01: data.name,
XPRD02: data.price,
XPRD05: data.categoryId
// PRD_ID and XPRD03 auto-populated
}
})
});

const result = await response.json();
console.log('Generated UUID:', result.data.PRD_ID);
console.log('Generated code:', result.data.XPRD03);

return result;
}

Pattern 3: Create with Validation

Handle validation errors gracefully:

async function createProductWithValidation(data) {
try {
const response = await fetch(`${apiBase}/api/v4/core/PRD`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ data })
});

if (!response.ok) {
const error = await response.json();
throw error;
}

return response.json();
} catch (error) {
switch (error.code) {
case 'REQUIRED_FIELD_MISSING':
alert(`Please fill in ${error.field}`);
break;
case 'FIELD_NOT_CREATEABLE':
alert(`Field ${error.field} cannot be set on creation`);
break;
case 'GRANT_DENIED':
alert('You do not have permission to create products');
break;
default:
alert('Product creation failed');
}
throw error;
}
}

Best Practices

✅ DO:

Let system manage automatic fields:

// ✅ Good - don't include auto fields
{
"data": {
"XPRD01": "Product Name",
"XPRD02": 99.99
// PRD_ID, XPRD03, TREC, CDATA auto-populated
}
}

Use cascades for master-detail:

// ✅ Good - single request with items
POST /api/v4/core/ORD
{
"data": {
"XORD01": "Order",
"items": [...]
}
}

Handle validation errors:

// ✅ Good
try {
await createProduct(data);
} catch (error) {
handleValidationError(error);
}

❌ DON'T:

Don't include auto-generated fields:

// ❌ Bad - trying to set auto fields
{
"data": {
"PRD_ID": "custom-id", // Auto-generated by uuid
"XPRD03": "custom-code", // Auto-generated by counter
"TREC": "N" // Auto-managed
}
}

Don't create detail records separately:

// ❌ Bad - separate requests (N+1 problem)
const order = await createOrder(orderData);
for (const item of items) {
await createOrderItem(order.ORD_ID, item);
}

// ✅ Good - single request with cascade
await createOrder({ ...orderData, items });

Summary

  • ✅ POST creates new records with TREC='N'
  • ✅ Auto-populated fields: PRD_ID (uuid), counters, timestamps, slugs, hashes
  • ✅ Required field validation via TB_COST.REQUIRED
  • ✅ COD_ON_OFF='N' flag required for field to be createable
  • ✅ INSERT_CASCADE creates related records automatically
  • ✅ Outbox event published to RabbitMQ
  • ✅ ACID transaction ensures all-or-nothing

Key Takeaways:

  1. Don't include auto-generated fields (uuid, counter, TREC, CDATA)
  2. Use INSERT_CASCADE for master-detail creation
  3. Handle validation errors (REQUIRED_FIELD_MISSING, FIELD_NOT_CREATEABLE)
  4. Trust pre-insert functions to populate fields
  5. Every creation publishes event for downstream consumers

Next: Update Operations →