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:
app_object_registriesdefines registry objects (includingobj_code: the logical object identifier tied to SQL / metadata).app_object_profilesdefines how a registry object is exposed for CRUD (permissions, projections, etc.).- The middleware exposes one generic entity router parameterized by
:entity_code— that value is the profile’sprf_code, not the registry’sobj_code. - The frontend calls these through
record.service(axiom-genesis-frontend/src/services/record.service.ts).
Registry vs profile codes (do not confuse)
| Column | Role |
|---|---|
app_object_registries.obj_code | Registry object key. Not the :entity_code URL segment. |
app_object_profiles.prf_code | Equals entity_code — use this string in /api/entity/:entity_code/... and in recordService.*('…'). |
app_object_profiles.prf_obj_code | Links the profile to its registry row: prf_obj_code = registry obj_code. |
So: obj_code ≠ prf_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/createGET /api/entity/:entity_code/getGET /api/entity/:entity_code/get_by_idGET /api/entity/:entity_code/get_by_filterPUT /api/entity/:entity_code/updateDELETE /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_configs → app_page_configs; workflow_configs → app_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.tsthat 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:
- Identify the correct entity_code.
- Replace with
recordService.getByFilter(entityCode, filters)orrecordService.get(entityCode, { params }). - Keep tenant + auth headers centralized in
apiClient.