Land a generated module by hand
This guide takes the files produced by Generate your first module and turns them into a running StackRivet page. It is written for the exact moment many beginners hit: the generator succeeded, generated-output/ is full of backend, frontend and permission files, but the new page is not visible in the admin UI yet.
The key idea is simple: Community generation is review-first. Apply writes a staged module bundle. You choose the Maven module, rename the permission seed into a Flyway migration, copy the Vue files, add a static route, then verify RBAC and the menu tree.
What You Will Wire
Section titled “What You Will Wire”For the tutorial table biz_todo_item, use these generation names before preview/apply:
- Strip prefix:
biz_sobiz_todo_itembecomes Java classTodoItem. - Module name:
todo, used for the frontend feature folder and RBAC prefix. - Feature name:
todoItem, used in permission tokens such astodo:todoItem:list. - Package name:
com.stackrivet.demo.todo, which lets the first exercise land inside the existingstackrivet-demoMaven module.
After apply, the default staged tree looks like this:
generated-output/ src/main/java/com/stackrivet/demo/todo/... src/test/java/com/stackrivet/demo/todo/... frontend/src/api/todo-item.api.ts frontend/src/features/todo/TodoItemList.vue frontend/src/features/todo/TodoItemFormDrawer.vue db/migration/todo__permissions.sql module.manifest.json docs/ai/modules/todo.mdtodo-item.api.ts calls the REST collection path /todo-items. The generated menu SQL uses the page path /todo-item. That singular/plural split is intentional: API collections are plural; admin routes are page slugs.
1. Choose The Maven Module
Section titled “1. Choose The Maven Module”Do not copy generated Java files into stackrivet-app, stackrivet-common or stackrivet-security just because those modules are visible. stackrivet-app is the Boot entrypoint, common is shared infrastructure, and security is authentication/RBAC infrastructure.
Use this decision table:
| Choice | Use it when | Required POM work |
|---|---|---|
| Existing domain module | The resource belongs to an existing bounded context. | Usually none. Confirm the module is already a stackrivet-app dependency. |
stackrivet-demo | You are learning or adding an example module. | No extra POM changes for this tutorial path: the root reactor and stackrivet-app already include it. |
New stackrivet-<domain> module | The feature deserves its own dependency boundary. | Add it to the root reactor, BOM and stackrivet-app dependencies. |
For the first hands-on run, use stackrivet-demo. It already has the dependencies a generated CRUD module needs: stackrivet-common, MyBatis-Plus, Spring Web, Spring Security core and OpenAPI annotations.
For a real domain module, create stackrivet-todo/pom.xml from stackrivet-demo/pom.xml, then update all three Maven places:
<modules> ... <module>stackrivet-todo</module> <module>stackrivet-app</module></modules><dependency> <groupId>com.stackrivet</groupId> <artifactId>stackrivet-todo</artifactId> <version>${stackrivet.version}</version></dependency><dependency> <groupId>com.stackrivet</groupId> <artifactId>stackrivet-todo</artifactId></dependency>The root reactor builds the module. The BOM lets other modules depend on it without writing a version. The app dependency puts the module’s classes and resources on the runtime classpath so Spring, MyBatis and Flyway can see them.
2. Copy Backend Files
Section titled “2. Copy Backend Files”For the tutorial path using stackrivet-demo, copy the generated Java and test sources from the backend repository root:
cd stackrivet-server
rsync -av generated-output/src/main/java/ stackrivet-demo/src/main/java/rsync -av generated-output/src/test/java/ stackrivet-demo/src/test/java/Then inspect the package:
find stackrivet-demo/src/main/java/com/stackrivet/demo/todo -type f | sortfind stackrivet-demo/src/test/java/com/stackrivet/demo/todo -type f | sortYou should see TodoItemEntity, TodoItemMapper, TodoItemService, TodoItemServiceImpl, TodoItemController, DTOs, a VO and a service test. The generated controller already has @PreAuthorize guards for todo:todoItem:{list,create,update,delete}.
Run a narrow backend check:
mvn -pl stackrivet-demo -am testIf you created a new module, replace stackrivet-demo with your artifact, for example:
mvn -pl stackrivet-todo -am test3. Rename The Flyway Permission Seed
Section titled “3. Rename The Flyway Permission Seed”The generated file is a seed, not a Flyway version yet:
generated-output/db/migration/todo__permissions.sqlFind the versions already used across all active migration locations:
find stackrivet-app/src/main/resources/db/migration -name 'V*__*.sql' | sortThe app migration tree in this repository uses V1__baseline.sql and V2__dashboard_count_indexes.sql in the vendor folders. For this tutorial, use the next integer:
cp generated-output/db/migration/todo__permissions.sql \ stackrivet-app/src/main/resources/db/migration/common/V3__seed_todo_permissions.sqlUse common/ when the SQL is dialect-neutral. The generated permission seed is written for both MySQL and PostgreSQL. If your own migration uses vendor-specific SQL, put matching files under mysql/ and postgresql/ instead, using the same business meaning for each database.
Before booting, review these fields in the SQL:
| Field | What to check |
|---|---|
id | MGEN_TODO must not collide with another sr_sys_menu.id. |
path | Default is /todo-item; it must match the Vue route you add. |
component | Default is features/todo/TodoItemList; it should match the copied Vue file. |
permission | todo:todoItem:list must match route meta and controller @PreAuthorize. |
visible / status | Page menu row should be TRUE and active; button rows are hidden but grant permissions. |
Do not edit an already-applied Flyway migration in a shared database. Add a new version if you need to adjust menu IDs, route paths or permissions later.
4. Copy Frontend Files
Section titled “4. Copy Frontend Files”From the admin UI repository root, copy the staged frontend files:
cd stackrivet-admin-ui
rsync -av ../stackrivet-server/generated-output/frontend/src/api/ src/api/rsync -av ../stackrivet-server/generated-output/frontend/src/features/ src/features/You should now have:
src/api/todo-item.api.tssrc/features/todo/TodoItemList.vuesrc/features/todo/TodoItemFormDrawer.vueOpen TodoItemList.vue and translate the local zh-CN labels if the database comments are English. The generated component intentionally keeps module title and field labels local to the component, so the first page works without editing the global locale files.
5. Add The Vue Route
Section titled “5. Add The Vue Route”Open src/router/index.ts and add a child route under the main "/" route:
{ path: "todo-item", name: "TodoItem", component: () => import("@/features/todo/TodoItemList.vue"), meta: { title: "Todo items", permission: "todo:todoItem:list", icon: "list" },},Match these three values:
| Place | Value |
|---|---|
sr_sys_menu.path | /todo-item |
Vue child route path | todo-item |
Route meta.permission | todo:todoItem:list |
The sidebar is built from the /api/v1/me menu tree, but it also filters out menu paths that do not resolve to a real frontend route. That is why a valid SQL seed still stays invisible until the route exists.
Run the frontend checks:
pnpm typecheckpnpm lint6. Boot And Verify
Section titled “6. Boot And Verify”Start the backend so Flyway applies V3__seed_todo_permissions.sql:
cd stackrivet-servermvn -pl stackrivet-app -am -DskipTests packagemvn -pl stackrivet-app spring-boot:runStart the admin UI:
cd stackrivet-admin-uipnpm devLog in with a user that has the seeded super_admin role, then open:
http://127.0.0.1:5173/todo-itemVerify both the API and the menu grant:
curl -H "Authorization: Bearer $TOKEN" \ http://127.0.0.1:8080/api/v1/todo-items
curl -H "Authorization: Bearer $TOKEN" \ http://127.0.0.1:8080/api/v1/me | jq '.permissions'You should see todo:todoItem:list, todo:todoItem:create, todo:todoItem:update and todo:todoItem:delete in the permissions.
7. If The Menu Is Invisible
Section titled “7. If The Menu Is Invisible”Use this checklist in order. It is faster than guessing.
| Symptom | Check | Fix |
|---|---|---|
Direct /todo-item opens 404 | Route missing or wrong path. | Add the child route, then reload Vite. |
Direct /todo-item opens 403 | Route exists, permission missing. | Grant todo:todoItem:list to the user’s role. |
| API returns 403 | Controller permission is not granted. | Grant the matching button/API permission rows. |
| API works but sidebar hides item | /me menu tree lacks the menu row, or route filter removed it. | Check role grants, visible, status and route path. |
| SQL exists but no user sees it | Flyway migration did not run on the database you are using. | Check the active datasource and Flyway history table. |
| It appears only in dev | The menu is under the examples group. | Move it to a product group or keep it top-level. |
Database checks:
SELECT id, parent_id, type, title, path, component, permission, visible, statusFROM sr_sys_menuWHERE id LIKE 'MGEN_TODO%' OR permission LIKE 'todo:%' OR path = '/todo-item';
SELECT role_id, menu_idFROM sr_sys_role_menuWHERE menu_id LIKE 'MGEN_TODO%';Frontend checks:
rg -n "TodoItem|todo-item|todo:todoItem" src/router src/features src/apiAuth checks:
curl -H "Authorization: Bearer $TOKEN" \ http://127.0.0.1:8080/api/v1/me \ | jq '{permissions, menus}'After changing a user’s role grants, log out and log in again. The admin UI stores only the token in local storage; the user profile, permission set and menu tree are refreshed from /me.
8. Common Beginner Mistakes
Section titled “8. Common Beginner Mistakes”| Mistake | Why it breaks |
|---|---|
Copying backend files into stackrivet-app | The app module is the runtime assembly, not the business boundary. |
Adding a root <module> but not an app dependency | Maven builds the module, but Spring Boot never loads it at runtime. |
| Adding an app dependency but not the BOM entry | The dependency may require an explicit version and fail the build. |
Leaving the permission SQL as todo__permissions.sql | Flyway ignores it because it is not a V...__...sql migration. |
Changing sr_sys_menu.path without changing Vue route | The sidebar drops the item because the route does not resolve. |
Changing route meta.permission without changing SQL/controller permissions | Navigation and API authorization no longer agree. |
Reusing MGEN_TODO for another module | sr_sys_menu.id collides, or role grants point at the wrong rows. |
Final Checklist
Section titled “Final Checklist”- Backend files are inside the chosen Maven module.
- New Maven module, if any, is listed in the root reactor, BOM and
stackrivet-app. - Permission SQL is renamed to the next Flyway version and placed in
common/,mysql/orpostgresql/. - Frontend API and feature files are copied into
stackrivet-admin-ui/src. src/router/index.tshas a route whose path and permission matchsr_sys_menu.mvn -pl <module> -am test,pnpm typecheckandpnpm lintpass./api/v1/mereturns the expected permissions and menu row.