Skip to main content

Entity CRUD Pattern (No New Routes)

Principle

Axiom Genesis is parameter-driven: CRUD is not implemented per table in the frontend as separate REST modules. Instead:

  1. app_object_registries defines registry objects (including obj_code: the logical object identifier tied to SQL / metadata).
  2. app_object_profiles defines how a registry object is exposed for CRUD (permissions, projections, etc.).
  3. The middleware exposes one generic entity router parameterized by :entity_code — that value is the profile’s prf_code, not the registry’s obj_code.
  4. The frontend calls these through record.service (axiom-genesis-frontend/src/services/record.service.ts).

Registry vs profile codes (do not confuse)

ColumnRole
app_object_registries.obj_codeRegistry object key. Not the :entity_code URL segment.
app_object_profiles.prf_codeEquals entity_code — use this string in /api/entity/:entity_code/... and in recordService.*('…').
app_object_profiles.prf_obj_codeLinks the profile to its registry row: prf_obj_code = registry obj_code.

So: obj_codeprf_code, and prf_code = entity_code, prf_obj_code = obj_code (registry side). When aligning the frontend to the database, resolve the correct prf_code for the tenant/profile in use (or follow middleware entity-resolution rules), not the raw obj_code alone.

Middleware surface

File: axiom-genesis-middleware/routes/v1/entity.routes.js

Representative operations (non-exhaustive):

  • POST /api/entity/:entity_code/create
  • GET /api/entity/:entity_code/get
  • GET /api/entity/:entity_code/get_by_id
  • GET /api/entity/:entity_code/get_by_filter
  • PUT /api/entity/:entity_code/update
  • DELETE /api/entity/:entity_code/delete
  • Bulk variants: create_many, update_many, delete_many, update_by_fields, etc.

Middleware attaches:

  • Token verification
  • Entity resolution / authorization (authorizeEntityAccess, registry-backed helpers)

Frontend usage

Import patterns:

import recordService from '@/services/record.service';

await recordService.get('page_configs', { params });
await recordService.update('page_configs', payload);

Base URL is composed by apiClient (NEXT_PUBLIC_API_URL + /api).

Extended methods caveat

record.service.ts also exposes namespaced helpers for workflow, scrumboard HTTP APIs, auth, etc. These exist for historical or specialized flows.

Agent rule: For new configuration or transactional entities that already exist in registry/profile, do not add parallel Axios paths — extend registry/profile and use entity methods.

Entity code naming (prf_code in URLs)

The :entity_code path segment must match app_object_profiles.prf_code for the profile that governs that table’s CRUD (same value you pass to recordService).

Profile prf_code values must match entity.constants.ts / app.constants.js (e.g. genesisone-cfg: page_configsapp_page_configs; workflow_configsapp_workflow_configs; dataview_configs / scrumboard_configs; fn_get_app_navigation_configs_by_tenant_role for role menus — see config-table-catalog.md). If a route segment does not match prf_code, use resolveRegistryAndProfile on the middleware or align registry/profile seed data.

Always confirm the active prf_code for the target tenant/profile (registry row joined via prf_obj_code = obj_code). When unsure, inspect app_object_profiles, Redis-cached entity metadata, or middleware entity resolution — do not assume obj_code is the URL code.

Anti-patterns

  • Adding routes/v1/foo.routes.js + controller for a table already modeled as an entity.
  • Adding src/services/fooCrud.service.ts that duplicates entity CRUD.
  • Hardcoding SQL in frontend (never).

Migration guidance for legacy callers

Some studio files still reference legacy paths such as /page_configs/get_by_filter?.... When modifying those areas:

  1. Identify the correct entity_code.
  2. Replace with recordService.getByFilter(entityCode, filters) or recordService.get(entityCode, { params }).
  3. Keep tenant + auth headers centralized in apiClient.