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:
- Don't include auto-generated fields (uuid, counter, TREC, CDATA)
- Use INSERT_CASCADE for master-detail creation
- Handle validation errors (REQUIRED_FIELD_MISSING, FIELD_NOT_CREATEABLE)
- Trust pre-insert functions to populate fields
- Every creation publishes event for downstream consumers
Next: Update Operations →
Related Concepts
- Counter Management - TB_COUNTER details
- Validation - Validation layers
- Cascade Operations - INSERT_CASCADE patterns
- Outbox Pattern - Event sourcing