Skip to content

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.

For the tutorial table biz_todo_item, use these generation names before preview/apply:

  • Strip prefix: biz_ so biz_todo_item becomes Java class TodoItem.
  • Module name: todo, used for the frontend feature folder and RBAC prefix.
  • Feature name: todoItem, used in permission tokens such as todo:todoItem:list.
  • Package name: com.stackrivet.demo.todo, which lets the first exercise land inside the existing stackrivet-demo Maven 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.md

todo-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.

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:

ChoiceUse it whenRequired POM work
Existing domain moduleThe resource belongs to an existing bounded context.Usually none. Confirm the module is already a stackrivet-app dependency.
stackrivet-demoYou 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> moduleThe 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:

pom.xml
<modules>
...
<module>stackrivet-todo</module>
<module>stackrivet-app</module>
</modules>
stackrivet-bom/pom.xml
<dependency>
<groupId>com.stackrivet</groupId>
<artifactId>stackrivet-todo</artifactId>
<version>${stackrivet.version}</version>
</dependency>
stackrivet-app/pom.xml
<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.

For the tutorial path using stackrivet-demo, copy the generated Java and test sources from the backend repository root:

Terminal window
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:

Terminal window
find stackrivet-demo/src/main/java/com/stackrivet/demo/todo -type f | sort
find stackrivet-demo/src/test/java/com/stackrivet/demo/todo -type f | sort

You 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:

Terminal window
mvn -pl stackrivet-demo -am test

If you created a new module, replace stackrivet-demo with your artifact, for example:

Terminal window
mvn -pl stackrivet-todo -am test

The generated file is a seed, not a Flyway version yet:

generated-output/db/migration/todo__permissions.sql

Find the versions already used across all active migration locations:

Terminal window
find stackrivet-app/src/main/resources/db/migration -name 'V*__*.sql' | sort

The 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:

Terminal window
cp generated-output/db/migration/todo__permissions.sql \
stackrivet-app/src/main/resources/db/migration/common/V3__seed_todo_permissions.sql

Use 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:

FieldWhat to check
idMGEN_TODO must not collide with another sr_sys_menu.id.
pathDefault is /todo-item; it must match the Vue route you add.
componentDefault is features/todo/TodoItemList; it should match the copied Vue file.
permissiontodo:todoItem:list must match route meta and controller @PreAuthorize.
visible / statusPage 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.

From the admin UI repository root, copy the staged frontend files:

Terminal window
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.ts
src/features/todo/TodoItemList.vue
src/features/todo/TodoItemFormDrawer.vue

Open 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.

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:

PlaceValue
sr_sys_menu.path/todo-item
Vue child route pathtodo-item
Route meta.permissiontodo: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:

Terminal window
pnpm typecheck
pnpm lint

Start the backend so Flyway applies V3__seed_todo_permissions.sql:

Terminal window
cd stackrivet-server
mvn -pl stackrivet-app -am -DskipTests package
mvn -pl stackrivet-app spring-boot:run

Start the admin UI:

Terminal window
cd stackrivet-admin-ui
pnpm dev

Log in with a user that has the seeded super_admin role, then open:

http://127.0.0.1:5173/todo-item

Verify both the API and the menu grant:

Terminal window
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.

Use this checklist in order. It is faster than guessing.

SymptomCheckFix
Direct /todo-item opens 404Route missing or wrong path.Add the child route, then reload Vite.
Direct /todo-item opens 403Route exists, permission missing.Grant todo:todoItem:list to the user’s role.
API returns 403Controller 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 itFlyway migration did not run on the database you are using.Check the active datasource and Flyway history table.
It appears only in devThe 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, status
FROM sr_sys_menu
WHERE id LIKE 'MGEN_TODO%'
OR permission LIKE 'todo:%'
OR path = '/todo-item';
SELECT role_id, menu_id
FROM sr_sys_role_menu
WHERE menu_id LIKE 'MGEN_TODO%';

Frontend checks:

Terminal window
rg -n "TodoItem|todo-item|todo:todoItem" src/router src/features src/api

Auth checks:

Terminal window
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.

MistakeWhy it breaks
Copying backend files into stackrivet-appThe app module is the runtime assembly, not the business boundary.
Adding a root <module> but not an app dependencyMaven builds the module, but Spring Boot never loads it at runtime.
Adding an app dependency but not the BOM entryThe dependency may require an explicit version and fail the build.
Leaving the permission SQL as todo__permissions.sqlFlyway ignores it because it is not a V...__...sql migration.
Changing sr_sys_menu.path without changing Vue routeThe sidebar drops the item because the route does not resolve.
Changing route meta.permission without changing SQL/controller permissionsNavigation and API authorization no longer agree.
Reusing MGEN_TODO for another modulesr_sys_menu.id collides, or role grants point at the wrong rows.
  • 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/ or postgresql/.
  • Frontend API and feature files are copied into stackrivet-admin-ui/src.
  • src/router/index.ts has a route whose path and permission match sr_sys_menu.
  • mvn -pl <module> -am test, pnpm typecheck and pnpm lint pass.
  • /api/v1/me returns the expected permissions and menu row.