Shadmin Feature Development
Guide full-stack feature development through Shadmin's clean architecture, producing code that compiles, passes lint/tests, and follows established patterns. Most features require both backend and frontend changes — this skill covers the end-to-end workflow.
Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ Frontend (React 19 + TypeScript + Vite) │
│ Route File → Page Component → TanStack Query Hook → API Service │
│ ↕ Zustand (auth-store) ↕ Permission checks │
├─────────────────────────────────────────────────────────────────────┤
│ HTTP (Axios apiClient ← Bearer Token injection) │
├─────────────────────────────────────────────────────────────────────┤
│ Backend (Go + Gin + Ent ORM) │
│ Route → [JWT MW → Casbin MW] → Controller → Usecase → Repository │
│ ↕ Domain (contracts, DTOs, errors) ↕ Ent (DB, migrations) │
└─────────────────────────────────────────────────────────────────────┘
Backend layers — each has exactly one responsibility:
| Layer |
Directory |
Responsibility |
| Domain |
domain/ |
Entity structs, DTOs, Repository/UseCase interfaces, errors, response helpers |
| Schema |
ent/schema/ |
DB schema → run go generate ./ent after changes |
| Repository |
repository/ |
Data access via Ent, domain↔ent conversion, pagination |
| Usecase |
usecase/ |
Business logic, validation, context.WithTimeout |
| Controller |
api/controller/ |
HTTP parsing only, Swagger annotations, status code mapping |
| Route |
api/route/ |
Route registration, middleware wiring |
| Factory |
api/route/factory.go |
DI: repo → usecase → controller construction |
| Bootstrap |
bootstarp/ |
App init, DB, Casbin, seeds (directory name typo is intentional) |
Frontend layers:
| Layer |
Directory |
Responsibility |
| Types |
frontend/src/types/ |
TypeScript interfaces matching backend DTOs |
| Services |
frontend/src/services/ |
Axios API wrappers, date parsing |
| Features |
frontend/src/features/ |
Page components, tables, dialogs, forms, hooks |
| Routes |
frontend/src/routes/ |
TanStack Router file-based routing |
| Stores |
frontend/src/stores/ |
Zustand state (auth, permissions) |
| Constants |
frontend/src/constants/ |
Permission strings, enums |
Full-Stack Development Workflow
Step 1: Clarify Scope (before writing code)
State explicitly:
- What entities/fields are involved
- API endpoints: path, method, request/response shapes
- Whether Casbin permission checks are needed
- Frontend: pages, tables, forms, dialogs
- Permission strings (e.g.,
system:project:add)
Step 2: List All Touched Files
Group by layer — this catches missing pieces early:
# Backend (implement in this order)
domain/<resource>.go
ent/schema/<resource>.go
repository/<resource>_repository.go
usecase/<resource>_usecase.go
api/controller/<resource>_controller.go
api/route/<resource>_routes.go (or modify system_routes.go)
api/route/factory.go
# Frontend (implement in this order)
frontend/src/types/<resource>.ts
frontend/src/services/<resource>Api.ts
frontend/src/features/<module>/<resource>/components/*-provider.tsx
frontend/src/features/<module>/<resource>/hooks/use-<resource>.ts
frontend/src/features/<module>/<resource>/components/*-columns.tsx
frontend/src/features/<module>/<resource>/components/*-table.tsx
frontend/src/features/<module>/<resource>/components/*-form-dialog.tsx
frontend/src/features/<module>/<resource>/components/*-dialogs.tsx
frontend/src/features/<module>/<resource>/components/*-primary-buttons.tsx
frontend/src/features/<module>/<resource>/data/schema.ts
frontend/src/features/<module>/<resource>/index.tsx
frontend/src/routes/_authenticated/<module>/<resource>.tsx
frontend/src/constants/permissions.ts (add new permission keys)
Step 3: Implement Backend
Follow the layer order strictly — each layer depends on the one above.
Read references/backend.md for complete code templates and patterns.
Quick reference for key conventions:
- IDs:
xid.New().String() in Ent schema DefaultFunc
- Partial updates: pointer fields in
Update*Request (*string)
- Pagination: embed
domain.QueryParams, call domain.ValidateQueryParams()
- Response:
domain.RespSuccess(data) (code=0) / domain.RespError(msg) (code=1)
- Usecase: every method starts with
context.WithTimeout + defer cancel()
- Errors: sentinel errors in domain,
%w wrapping, map to HTTP status in controller
- Factory: repo → usecase → controller, dependencies from
f.db, f.app, f.timeout
- Routes: protected system routes use
casbinMiddleware.CheckAPIPermission()
Step 4: Implement Frontend
Follow the order: types → service → feature module → route file.
Read references/frontend.md for complete code templates and patterns.
Quick reference for key conventions:
- API response:
response.data.data (outer .data = Axios, inner .data = domain.Response.Data)
- Date parsing: API service converts string dates to
Date objects
- Query params:
URLSearchParams construction, snake_case to match backend
- Table state:
useTableUrlState hook syncs pagination/filters with URL
- Dialog state: string-based via context provider (
open === 'add' | 'edit' | 'delete')
- Permissions:
usePermission() hook, PERMISSIONS.SYSTEM.RESOURCE.ACTION constants
- Toast:
sonner for success/error notifications
- Forms: React Hook Form + Zod, single hook handles create/edit
- Route file: Zod schema validates URL search params with
.catch() defaults
Step 5: Wire Permissions
Shadmin uses a dual-layer permission model:
Backend (API access): Casbin checks (userID, path, method)
Frontend (UI visibility): Permission strings like "system:project:add"
These are linked through the Role → Menu → API Resources binding:
- Backend auto-scans routes into API resources on startup (
bootstrap.InitApiResources)
- API resource IDs are deterministic:
METHOD:/api/v1/path (e.g., GET:/api/v1/system/project)
- Admin assigns menus to roles, each menu binds to API resources
- Frontend fetches permissions from
/api/v1/resources and stores in Zustand
To add permissions for a new feature:
- Backend: routes auto-register as API resources on restart
- Frontend: add permission constants in
frontend/src/constants/permissions.ts
- Admin panel: create menu entries, bind API resources, assign to roles
Step 6: Generate & Verify
# Backend
go generate ./ent # If schema changed
go fmt ./... && go vet ./... # Format + static analysis
go test ./... # Run tests
swag init -g main.go --output ./docs # If Swagger annotations changed
# Frontend (from frontend/)
pnpm lint # ESLint
pnpm format:check # Prettier
pnpm build # Recommended if routes/build config changed
Key Response Format
// Success (HTTP 200/201)
type Response struct {
Code int `json:"code"` // 0 = success
Msg string `json:"msg"` // "OK"
Data interface{} `json:"data"` // payload
}
// Error (HTTP 400/404/500)
// Code = 1, Msg = error description, Data = nil
// Paginated response (in Data field)
type PagedResult[T any] struct {
List []T `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
}
Boundaries
Things to never do:
- No business logic in controllers — controllers parse HTTP, call usecase, return response
- No HTTP/permission logic in repositories — repositories do data access only
- No bypassing Casbin on protected APIs
- No new globals — use factory's
f.db, f.app, f.timeout
- No inline API calls in React — all API access goes through
services/ wrappers
- No direct localStorage for auth — use
useAuthStore
- No editing
components/ui/ — shadcn-generated primitives
- No hardcoded menus — menus come from backend
/api/v1/resources
- No unnecessary dependencies — frontend or backend
- Minimal changes — only touch files relevant to the feature
Reference Files
For detailed code templates and implementation patterns, read these as needed:
references/backend.md — Complete Go/Gin/Ent code templates for domain, schema, repository, usecase, controller, routes, and factory. Read when implementing backend features.
references/frontend.md — Complete React/TypeScript code templates for types, API services, feature modules, hooks, tables, forms, dialogs, routes, and permissions. Read when implementing frontend features.
Further Documentation
The docs/getting-started/ directory contains comprehensive guides:
quickstart.zh.md / quickstart.en.md — Quick start guide
architecture.zh.md / architecture.en.md — Architecture deep-dive
development.zh.md / development.en.md — Full CRUD walkthrough with example
deployment.zh.md / deployment.en.md — Production deployment guide
Source: ahaodev/shadmin — distributed by TomeVault.
1---2name: shadmin-dev3description: Apply Shadmin feature-development standards (backend Go/Gin/Ent + frontend React/TS). Use when adding/modifying features, CRUD modules, API routes/controllers/usecases/repositories, Ent schemas, frontend pages/routes, React components, TanStack hooks, or any full-stack work in this project. Trigger whenever the user mentions new features, backend changes, frontend changes, database schema changes, permissions, UI pages, tables, forms, or API endpoints — even if they don't explicitly say "feature development. Use when this capability is needed.4---56# Shadmin Feature Development78Guide full-stack feature development through Shadmin's clean architecture, producing code that compiles, passes lint/tests, and follows established patterns. Most features require both backend and frontend changes — this skill covers the end-to-end workflow.910## Architecture Overview1112```13┌─────────────────────────────────────────────────────────────────────┐14│ Frontend (React 19 + TypeScript + Vite) │15│ Route File → Page Component → TanStack Query Hook → API Service │16│ ↕ Zustand (auth-store) ↕ Permission checks │17├─────────────────────────────────────────────────────────────────────┤18│ HTTP (Axios apiClient ← Bearer Token injection) │19├─────────────────────────────────────────────────────────────────────┤20│ Backend (Go + Gin + Ent ORM) │21│ Route → [JWT MW → Casbin MW] → Controller → Usecase → Repository │22│ ↕ Domain (contracts, DTOs, errors) ↕ Ent (DB, migrations) │23└─────────────────────────────────────────────────────────────────────┘24```2526**Backend layers** — each has exactly one responsibility:2728| Layer | Directory | Responsibility |29|-------|-----------|---------------|30| Domain | `domain/` | Entity structs, DTOs, Repository/UseCase interfaces, errors, response helpers |31| Schema | `ent/schema/` | DB schema → run `go generate ./ent` after changes |32| Repository | `repository/` | Data access via Ent, domain↔ent conversion, pagination |33| Usecase | `usecase/` | Business logic, validation, `context.WithTimeout` |34| Controller | `api/controller/` | HTTP parsing only, Swagger annotations, status code mapping |35| Route | `api/route/` | Route registration, middleware wiring |36| Factory | `api/route/factory.go` | DI: repo → usecase → controller construction |37| Bootstrap | `bootstarp/` | App init, DB, Casbin, seeds (directory name typo is intentional) |3839**Frontend layers:**4041| Layer | Directory | Responsibility |42|-------|-----------|---------------|43| Types | `frontend/src/types/` | TypeScript interfaces matching backend DTOs |44| Services | `frontend/src/services/` | Axios API wrappers, date parsing |45| Features | `frontend/src/features/` | Page components, tables, dialogs, forms, hooks |46| Routes | `frontend/src/routes/` | TanStack Router file-based routing |47| Stores | `frontend/src/stores/` | Zustand state (auth, permissions) |48| Constants | `frontend/src/constants/` | Permission strings, enums |4950## Full-Stack Development Workflow5152### Step 1: Clarify Scope (before writing code)5354State explicitly:55- What entities/fields are involved56- API endpoints: path, method, request/response shapes57- Whether Casbin permission checks are needed58- Frontend: pages, tables, forms, dialogs59- Permission strings (e.g., `system:project:add`)6061### Step 2: List All Touched Files6263Group by layer — this catches missing pieces early:6465```66# Backend (implement in this order)67domain/<resource>.go68ent/schema/<resource>.go69repository/<resource>_repository.go70usecase/<resource>_usecase.go71api/controller/<resource>_controller.go72api/route/<resource>_routes.go (or modify system_routes.go)73api/route/factory.go7475# Frontend (implement in this order)76frontend/src/types/<resource>.ts77frontend/src/services/<resource>Api.ts78frontend/src/features/<module>/<resource>/components/*-provider.tsx79frontend/src/features/<module>/<resource>/hooks/use-<resource>.ts80frontend/src/features/<module>/<resource>/components/*-columns.tsx81frontend/src/features/<module>/<resource>/components/*-table.tsx82frontend/src/features/<module>/<resource>/components/*-form-dialog.tsx83frontend/src/features/<module>/<resource>/components/*-dialogs.tsx84frontend/src/features/<module>/<resource>/components/*-primary-buttons.tsx85frontend/src/features/<module>/<resource>/data/schema.ts86frontend/src/features/<module>/<resource>/index.tsx87frontend/src/routes/_authenticated/<module>/<resource>.tsx88frontend/src/constants/permissions.ts (add new permission keys)89```9091### Step 3: Implement Backend9293Follow the layer order strictly — each layer depends on the one above.9495**Read `references/backend.md` for complete code templates and patterns.**9697Quick reference for key conventions:98- **IDs**: `xid.New().String()` in Ent schema `DefaultFunc`99- **Partial updates**: pointer fields in `Update*Request` (`*string`)100- **Pagination**: embed `domain.QueryParams`, call `domain.ValidateQueryParams()`101- **Response**: `domain.RespSuccess(data)` (code=0) / `domain.RespError(msg)` (code=1)102- **Usecase**: every method starts with `context.WithTimeout` + `defer cancel()`103- **Errors**: sentinel errors in domain, `%w` wrapping, map to HTTP status in controller104- **Factory**: repo → usecase → controller, dependencies from `f.db`, `f.app`, `f.timeout`105- **Routes**: protected system routes use `casbinMiddleware.CheckAPIPermission()`106107### Step 4: Implement Frontend108109Follow the order: types → service → feature module → route file.110111**Read `references/frontend.md` for complete code templates and patterns.**112113Quick reference for key conventions:114- **API response**: `response.data.data` (outer `.data` = Axios, inner `.data` = `domain.Response.Data`)115- **Date parsing**: API service converts string dates to `Date` objects116- **Query params**: `URLSearchParams` construction, snake_case to match backend117- **Table state**: `useTableUrlState` hook syncs pagination/filters with URL118- **Dialog state**: string-based via context provider (`open === 'add' | 'edit' | 'delete'`)119- **Permissions**: `usePermission()` hook, `PERMISSIONS.SYSTEM.RESOURCE.ACTION` constants120- **Toast**: `sonner` for success/error notifications121- **Forms**: React Hook Form + Zod, single hook handles create/edit122- **Route file**: Zod schema validates URL search params with `.catch()` defaults123124### Step 5: Wire Permissions125126Shadmin uses a **dual-layer permission model**:127128```129Backend (API access): Casbin checks (userID, path, method)130Frontend (UI visibility): Permission strings like "system:project:add"131```132133These are linked through the **Role → Menu → API Resources** binding:1341. Backend auto-scans routes into API resources on startup (`bootstrap.InitApiResources`)1352. API resource IDs are deterministic: `METHOD:/api/v1/path` (e.g., `GET:/api/v1/system/project`)1363. Admin assigns menus to roles, each menu binds to API resources1374. Frontend fetches permissions from `/api/v1/resources` and stores in Zustand138139**To add permissions for a new feature:**1401. Backend: routes auto-register as API resources on restart1412. Frontend: add permission constants in `frontend/src/constants/permissions.ts`1423. Admin panel: create menu entries, bind API resources, assign to roles143144### Step 6: Generate & Verify145146```bash147# Backend148go generate ./ent # If schema changed149go fmt ./... && go vet ./... # Format + static analysis150go test ./... # Run tests151swag init -g main.go --output ./docs # If Swagger annotations changed152153# Frontend (from frontend/)154pnpm lint # ESLint155pnpm format:check # Prettier156pnpm build # Recommended if routes/build config changed157```158159## Key Response Format160161```go162// Success (HTTP 200/201)163type Response struct {164 Code int `json:"code"` // 0 = success165 Msg string `json:"msg"` // "OK"166 Data interface{} `json:"data"` // payload167}168169// Error (HTTP 400/404/500)170// Code = 1, Msg = error description, Data = nil171172// Paginated response (in Data field)173type PagedResult[T any] struct {174 List []T `json:"list"`175 Total int `json:"total"`176 Page int `json:"page"`177 PageSize int `json:"page_size"`178 TotalPages int `json:"total_pages"`179}180```181182## Boundaries183184Things to never do:185- **No business logic in controllers** — controllers parse HTTP, call usecase, return response186- **No HTTP/permission logic in repositories** — repositories do data access only187- **No bypassing Casbin** on protected APIs188- **No new globals** — use factory's `f.db`, `f.app`, `f.timeout`189- **No inline API calls in React** — all API access goes through `services/` wrappers190- **No direct localStorage for auth** — use `useAuthStore`191- **No editing `components/ui/`** — shadcn-generated primitives192- **No hardcoded menus** — menus come from backend `/api/v1/resources`193- **No unnecessary dependencies** — frontend or backend194- **Minimal changes** — only touch files relevant to the feature195196## Reference Files197198For detailed code templates and implementation patterns, read these as needed:199200- **`references/backend.md`** — Complete Go/Gin/Ent code templates for domain, schema, repository, usecase, controller, routes, and factory. Read when implementing backend features.201- **`references/frontend.md`** — Complete React/TypeScript code templates for types, API services, feature modules, hooks, tables, forms, dialogs, routes, and permissions. Read when implementing frontend features.202203## Further Documentation204205The `docs/getting-started/` directory contains comprehensive guides:206- `quickstart.zh.md` / `quickstart.en.md` — Quick start guide207- `architecture.zh.md` / `architecture.en.md` — Architecture deep-dive208- `development.zh.md` / `development.en.md` — Full CRUD walkthrough with example209- `deployment.zh.md` / `deployment.en.md` — Production deployment guide210211---212> Source: [ahaodev/shadmin](https://github.com/ahaodev/shadmin) — distributed by [TomeVault](https://tomevault.io).213<!-- tomevault:4.0:skill_md:2026-07-04 -->