Admin Architecture
TL;DR - instant multi-page, multi-table apps
Feature | Provides | Example |
---|---|---|
Multi-Page | List/Show pages for each table Built-in search, sort, export |
Customer Page, Order Page, etc |
Multi-Table | Tab Sheet for related child data Page Transitions for related data |
Customer page has OrderList Click --> Order/OrderDetails |
Automatic Joins for Parent Data | Product Name - not just the Id | |
Lookups for foreign keys | Find Product for OrderDetail | |
Declarative hide/show | Salaried Employee: Hide Union | |
Customize | Simple - no HTML / JavaScript | Edit ui/admin/admin.yaml |
Extend | Build Custom UIs with your own framework, using the API | API-enforced logic simplifies app dev |
This page describes the Admin App architecture.
Uses Global React Admin
The admin "app" created in your project is just a yaml file. It is interpreted by a React Admin app (SAFRS React Admin - SRA by Thomas Pollet) that is part of the API Logic Server install, eg:
venv/lib/python3.12/site-packages/api_logic_server_cli/create_from_model/safrs-react-admin-npm-build
This contains the SRA code and the many react libraries it uses.
Customized Apps
The objective of the Admin App is to create an highly functional app instantly, easily customized via the yaml (instead of more complex HTML, JS, CSS, etc).
To create a more customized app, you can:
- Use the Automatic Custom Apps, or
- Create your own custom app using the API - by manual coding, or using Generative AI ("Vibe")
Generative AI App Learning
A close approximation of the automatic Admin App can be generated with GenAI, using this prompt:
Context
Generate a full React Admin application using the following instructions.
The result must be a runnable React app (npm start
) that connects to the supplied JSON:API, with fully implemented components (no placeholders or empty files).
App Features
Here is a sample App Page:
Multi-Page
For each resource: - Create a List page showing 7 user-friendly columns - Add pagination, sorting, and filtering - Link each row to a Display (Show) page
Multi-Resource
Each Display Page should:
- Show all fields in a multi-column layout
- Include a tab sheet (<TabbedShowLayout>
) for each related resource using <ReferenceManyField>
- Link child rows to their own display page
Example:
- Customer Display has tab for OrderList
- Each Order in the tab links to Order Display
Automatic Joins
For foreign keys:
- Display joined value (e.g., product.name
instead of product_id
)
- Use first string field from parent table containing name
, title
, or description
Numeric Primary key fields: - Display at the end of forms/lists
Lookups (Foreign Keys)
For foreign key fields:
- Provide auto-complete dropdown (<ReferenceInput>
)
- For numeric foreign keys, use the joined string field as lookup text
Implementation
Architecture
- Framework: React 18 + react-admin 4.x
- Data Provider: Custom
dataProvider.js
usingfetchUtils
(no externalra-jsonapi-client
) - Must support:
getList
,getOne
,getMany
,getManyReference
,create
,update
,delete
- Must support: filters, joins, sorting, pagination
- Backend: JSON:API per
mcp_discovery.json
- CORS: Ensure API allows
http://localhost:3000
- Project Setup:
- Use
create-react-app
- Include:
react-admin
,@mui/material
,@emotion/react
,@emotion/styled
,react-router-dom
- Do not use any deprecated or unmaintained libraries
- Include complete and correct
App.js
,index.js
,dataProvider.js
, andindex.html
Per-Resource Files (Required)
For each resource (Customer
, Order
etc):
- Create a source file under src/
, e.g., Customer.js
- Each file must fully implement:
- CustomerList
- CustomerShow
- CustomerCreate
- CustomerEdit
Use:
- <ReferenceField>
for foreign key displays
- <ReferenceInput>
for foreign key input
- <ReferenceManyField>
for tabbed child lists
- <TabbedShowLayout>
for display pages
Do not leave any file empty.
App Wiring
In App.js
:
- Import each resource file using:
- Register them in
<Admin>
using:
<Resource name="Customer" list={CustomerList} show={CustomerShow} edit={CustomerEdit} create={CustomerCreate} />
- import data provider (pre-supplied in src):
Do Not generate either:
import jsonServerProvider from 'ra-data-json-server'
const dataProvider = jsonServerProvider('http://api.example.com');
Response Format
Format the response as a JSResponseFormat:
class JSResponseFormat(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt code : str # generated javascript code (only)
GenAI Resources
If you are using Generative AI:
- Create
docs/mcp_learning/mcp_discovery.json
as the output of mcp_discovery, and provide that to your Generative AI tool. To obtain your discovery:
Here is an example of MCP Discovery:
{
"base_url": "http://localhost:5656/api",
"authorization_url": "http://localhost:5656/api/auth/login",
"description": "API Logic Project: basic_demo",
"learning": "To issue one request per row from a prior step (fan-out), use the syntax:\n\n\"$<stepIndex>[*].<fieldName>\"\n\nFor example, if step 0 returns orders, and you want to send a POST per customer:\n\nBefore (incorrect):\n \"customer_id\": \"{customer_id}\"\n\nAfter (correct):\n \"customer_id\": \"$0[*].customer_id\"\n \nOnly if 'email' is in the Natural language query, send email by issing a POST request to the SysEmail endpoint, setting the subject, message and customer_id in the body. DO NOT include a POST unless the word \u2018email\u2019 appears in the user query.\n\nFormat response as mcp_responseFormat.\n\n<mcp_responseFormat>\nclass QueryParam(BaseModel):\n name: str # attribute name\n op: str # eq, lt, gt\n val: str\n\nclass JsonValues(BaseMopdel):\n name: str # name of attribute\n value: str # value of attribute\n\nclass Resource(BaseModel):\n tool_type: str\n base_url: str\n path: str # specified use case or requirement name (use 'General' if missing)\n method: str # GET, PATCH, POST or DELETE\n body: json # data for PATCH or POST\n query_params: List(QueryParam) # filter for GET\n\nclass MCPResult(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt\n schema_version: str\n resources : List[Resource] # list resources\n\n<mcp_responseFormat/>\n",
"resources": [
{
"fields": [
"id",
"name",
"balance",
"credit_limit",
"email",
"email_opt_out"
],
"filterable": [
"id",
"name",
"balance",
"credit_limit",
"email",
"email_opt_out"
],
"methods": [
"GET",
"PATCH",
"POST",
"DELETE"
],
"name": "Customer",
"path": "/Customer"
},
{
"fields": [
"id",
"order_id",
"product_id",
"quantity",
"amount",
"unit_price"
],
"filterable": [
"id",
"order_id",
"product_id",
"quantity",
"amount",
"unit_price"
],
"methods": [
"GET",
"PATCH",
"POST",
"DELETE"
],
"name": "Item",
"path": "/Item"
},
{
"fields": [
"id",
"notes",
"customer_id",
"CreatedOn",
"date_shipped",
"amount_total"
],
"filterable": [
"id",
"notes",
"customer_id",
"CreatedOn",
"date_shipped",
"amount_total"
],
"methods": [
"GET",
"PATCH",
"POST",
"DELETE"
],
"name": "Order",
"path": "/Order"
},
{
"fields": [
"id",
"name",
"unit_price"
],
"filterable": [
"id",
"name",
"unit_price"
],
"methods": [
"GET",
"PATCH",
"POST",
"DELETE"
],
"name": "Product",
"path": "/Product"
}
],
"schema_version": "1.0",
"tool_type": "json-api"
}
- In addition, provide the schema (
docs/db.dbml
)
Here is an example of the schema:
// Copy this text, paste to https://dbdiagram.io/d
// Or, https://databasediagram.com/app
// Or, view in VSCode with extension: "DBML Live Preview"
Table Customer {
id INTEGER [primary key]
name VARCHAR
balance DECIMAL
credit_limit DECIMAL
email VARCHAR
email_opt_out BOOLEAN
}
Table Item {
id INTEGER [primary key]
order_id INTEGER
product_id INTEGER
quantity INTEGER
amount DECIMAL
unit_price DECIMAL
}
Table Order {
id INTEGER [primary key]
notes VARCHAR
customer_id INTEGER
CreatedOn DATE
date_shipped DATE
amount_total DECIMAL
}
Table Product {
id INTEGER [primary key]
name VARCHAR
unit_price DECIMAL
}
// Relationships
Ref: Item.(order_id) < Order.(id)
Ref: Item.(product_id) < Product.(id)
Ref: Order.(customer_id) < Customer.(id)