-
Notifications
You must be signed in to change notification settings - Fork 0
Creating a Module
This is the full walkthrough for building a new Prism module from scratch. By the end you'll have a working page served at /<your-name>.
Inside backend/src/modules/, create a folder with your module's name. The folder name becomes the URL.
backend/src/modules/kanban/
→ Served at http://localhost:3000/kanban
Naming rules:
- lowercase, no spaces
- hyphens are fine (
task-board,my-crm) - the folder name is the URL — pick something clean
This is the backend entry point. It must export a default AppModule.
// backend/src/modules/kanban/index.ts
import path from 'path'
import fs from 'fs'
import type { AppModule } from '../../shared/types/module'
const KanbanModule: AppModule = {
name: 'kanban',
version: '1.0.0',
async register(server, services, prefix) {
// prefix = "/kanban" — automatically derived from your folder name
const publicDir = path.join(process.cwd(), 'src', 'modules', 'kanban', 'public')
const assetPrefix = `${prefix}-assets`
// Serve your HTML page at /kanban
server.get(prefix, { config: { public: true } } as never, async (_req, reply) => {
const html = fs.readFileSync(path.join(publicDir, 'index.html'), 'utf-8')
.replaceAll('{{ASSETS}}', assetPrefix)
reply.type('text/html').send(html)
})
// Add your API routes under /kanban/api/...
server.get(`${prefix}/api/boards`, { config: { public: true } } as never, async () => {
return { boards: [] } // replace with real db queries
})
}
}
export default KanbanModuleYour UI. Use {{ASSETS}} wherever you reference a CSS or JS file — it gets replaced with the correct path at serve time.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Kanban</title>
<link rel="stylesheet" href="{{ASSETS}}/style.css" />
</head>
<body>
<h1>My Kanban Board</h1>
<div id="app"></div>
<script src="{{ASSETS}}/app.js"></script>
</body>
</html>Your frontend logic. Use window.location.pathname as the API base so your fetch calls route correctly regardless of what the module is named.
// The current page path — e.g. "/kanban"
// Prepend this to all API calls so they hit your module's routes
const API = window.location.pathname.replace(/\/$/, '')
async function loadBoards() {
const res = await fetch(API + '/api/boards')
const data = await res.json()
console.log(data.boards)
}
document.addEventListener('DOMContentLoaded', loadBoards)Plain CSS. Reference it from your HTML via {{ASSETS}}/style.css.
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 24px;
}npm run devOpen http://localhost:3000/kanban. Your page is live.
The plugin loader scans src/modules/ on every startup and registers everything it finds. No config changes, no manual wiring.
backend/src/modules/kanban/
├── index.ts
└── public/
├── index.html
├── app.js
└── style.css
- Module-Structure — Full reference for the AppModule contract
- Frontend-Guide — Patterns for building the UI
-
Core-Services — What
services.db,services.timer, etc. give you - Database-and-Prisma — Adding your own database models