Create a Full-Stack Plugin
AI prompt to generate a complete NaaP plugin with frontend, backend API, and database.
When to Use This#
Use this prompt when you need a plugin with:
- Its own backend API server (Express.js)
- A PostgreSQL database (with Prisma ORM)
- Full CRUD operations (create, read, update, delete)
- Server-side logic, scheduled tasks, or external API integrations
The Prompt#
Copy the entire block below and paste it into your AI assistant:
| 1 | # Task: Create a NaaP Full-Stack Plugin |
| 2 | |
| 3 | ## Context: NaaP Platform |
| 4 | |
| 5 | NaaP (Network as a Platform) is a plugin-based platform. Plugins can have a |
| 6 | React frontend, Express backend, and PostgreSQL database. |
| 7 | |
| 8 | Key technical details: |
| 9 | - Frontend: React 18+, TypeScript, Tailwind CSS, Vite (UMD bundle via createPluginConfig) |
| 10 | - Backend: Express.js, TypeScript, Prisma ORM |
| 11 | - Database: PostgreSQL (via Prisma) |
| 12 | - Shell integration: @naap/plugin-sdk |
| 13 | - Backend runs on its own port in local dev (`plugin.json → backend.devPort` is the single source of truth); on Vercel, the shell proxies API calls to /api/v1/[plugin-name]/* via dedicated Next.js route handlers |
| 14 | |
| 15 | **CRITICAL — Deployment-Safe API Patterns:** |
| 16 | - NEVER hardcode `http://localhost:PORT` in frontend code for API calls |
| 17 | - NEVER use `window.location.hostname + ':PORT'` for API URLs |
| 18 | - NEVER import or reference port numbers directly — `plugin.json → backend.devPort` is the single source of truth for ports; the SDK resolves them for you |
| 19 | - ALWAYS use one of these two SDK functions: |
| 20 | - `getPluginBackendUrl(name, { apiPath })` — returns a prefix you append relative paths to (e.g., `/posts`) |
| 21 | - `getServiceOrigin(name)` — returns just the origin; use when you build full paths (e.g., `/api/v1/registry/packages`) |
| 22 | - For pre-built clients, use `createShellApiClient()` or `createIntegrationClient()` from `@naap/plugin-sdk` — they use `getServiceOrigin('base')` internally |
| 23 | - In React components, prefer `usePluginApi()` hook which handles URL resolution |
| 24 | - On Vercel, there are NO separate backend ports — all API traffic is same-origin via relative paths (`/api/v1/...`) |
| 25 | - NEVER call `getPluginBackendUrl('base')` and then append `/api/v1/...` — this creates doubled URLs in production |
| 26 | |
| 27 | **CRITICAL — Next.js Route Handlers (Vercel/Production):** |
| 28 | - Every plugin API endpoint **must** have a dedicated Next.js route handler at |
| 29 | `apps/web-next/src/app/api/v1/{plugin-name}/...` |
| 30 | - The catch-all proxy (`/api/v1/[plugin]/[...path]`) returns **501 Not Implemented** |
| 31 | on Vercel for any unhandled route — it is NOT a passthrough |
| 32 | - When adding a new backend endpoint, always create a matching route handler file |
| 33 | - There are currently 46+ dedicated route handlers covering all plugin endpoints |
| 34 | |
| 35 | **CRITICAL — Build Configuration:** |
| 36 | - Use `createPluginConfig()` from `@naap/plugin-build/vite` in vite.config.ts |
| 37 | - Do NOT add a `postcss.config.js` to your plugin — PostCSS (tailwindcss, |
| 38 | autoprefixer) is configured centrally in the shared Vite config |
| 39 | - DO keep a `tailwind.config.js` in your frontend directory (for content paths) |
| 40 | |
| 41 | Available SDK hooks (import from @naap/plugin-sdk): |
| 42 | - useAuth() → { user, token, hasRole(role), hasPermission(perm) } |
| 43 | - useNotify() → { success(msg), error(msg), warning(msg), info(msg) } |
| 44 | - usePluginApi() → { get(url), post(url, data), put(url, data), delete(url) } |
| 45 | - URLs are relative: api.get('/items') calls /api/v1/[plugin-name]/items |
| 46 | - useThemeService() → { theme, isDark } |
| 47 | - useEvents() → { emit(event, data), on(event, callback) } |
| 48 | - usePluginContext() → { pluginId, teamId, config } |
| 49 | |
| 50 | Mount function pattern (frontend/src/mount.tsx): |
| 51 | ```tsx |
| 52 | import { createRoot } from 'react-dom/client'; |
| 53 | import { ShellProvider } from '@naap/plugin-sdk'; |
| 54 | import App from './App'; |
| 55 | |
| 56 | export function mount(container: HTMLElement, shellContext: any) { |
| 57 | const root = createRoot(container); |
| 58 | root.render( |
| 59 | <ShellProvider context={shellContext}> |
| 60 | <App /> |
| 61 | </ShellProvider> |
| 62 | ); |
| 63 | return () => root.unmount(); |
| 64 | } |
IMPORTANT — Database Architecture:
NaaP uses a single PostgreSQL database (naap) with multi-schema isolation.
All models are defined in packages/database/prisma/schema.prisma, NOT in
plugin directories. Every model MUST have @@schema("plugin_<name>") and
a prefix (e.g., MyPluginTask). See Database Architecture.
Backend db client (backend/src/db/client.ts):
import { prisma } from '@naap/database';
export const db = prisma;Backend server pattern (backend/src/server.ts):
| 1 | import express from 'express'; |
| 2 | import { db } from './db/client.js'; |
| 3 | |
| 4 | const app = express(); |
| 5 | const PORT = process.env.PORT || 4XX; // must match plugin.json → backend.devPort |
| 6 | |
| 7 | app.use(express.json()); |
| 8 | |
| 9 | // Health check — required by the shell |
| 10 | app.get('/health', (_req, res) => { |
| 11 | res.json({ status: 'ok', plugin: '[plugin-name]' }); |
| 12 | }); |
| 13 | |
| 14 | // Your routes here (use `db.myPrefixedModel.findMany()`, etc.) |
| 15 | |
| 16 | app.listen(PORT, () => { |
| 17 | console.log(`[plugin-name] backend running on port ${PORT}`); |
| 18 | }); |
Requirements#
Create a full-stack NaaP plugin called "[YOUR_PLUGIN_NAME]" that does:
[DESCRIBE WHAT YOUR PLUGIN SHOULD DO HERE — for example:
- "A project management tool with tasks, assignees, and due dates"
- "An inventory tracker for warehouse items with categories and stock levels"
- "A customer feedback board where users can submit and vote on ideas"
- "A time tracking app that logs hours per project" ]
Technical Specifications#
-
Generate plugin.json manifest:
JSON1 { 2 "name": "[plugin-name]", 3 "displayName": "[Plugin Display Name]", 4 "version": "1.0.0", 5 "description": "[description]", 6 "category": "[pick: analytics|communication|developer-tools|finance|infrastructure|monitoring|other]", 7 "author": { "name": "[Your Name]" }, 8 "frontend": { 9 "entry": "production/[plugin-name].js", 10 "routes": [{ "path": "/[plugin-name]", "component": "App" }], 11 "navigation": { "label": "[Display Name]", "icon": "[lucide-icon]", "order": 50 } 12 }, 13 "backend": { 14 "entry": "dist/server.js", 15 "devPort": 4XX, 16 "apiPrefix": "/api/v1/[plugin-name]", 17 "healthCheck": "/health" 18 }, 19 "database": { 20 "type": "postgresql", 21 "schema": "backend/prisma/schema.prisma" 22 }, 23 "permissions": ["read:profile", "read:team"] 24 } -
Generate full file structure:
1 [plugin-name]/ 2 ├── plugin.json 3 ├── frontend/ 4 │ ├── package.json 5 │ ├── tsconfig.json 6 │ ├── vite.config.ts # UMD build config (createPluginConfig) 7 │ ├── src/ 8 │ │ ├── App.tsx 9 │ │ ├── mount.tsx 10 │ │ ├── components/ 11 │ │ └── hooks/ (custom hooks if needed) 12 │ └── index.html 13 ├── backend/ 14 │ ├── package.json (depends on @naap/database, NOT @prisma/client) 15 │ ├── tsconfig.json 16 │ ├── .env (DATABASE_URL=...localhost:5432/naap) 17 │ └── src/ 18 │ ├── server.ts (Express app) 19 │ ├── routes/ (route handlers) 20 │ └── db/ 21 │ └── client.ts (re-exports prisma from @naap/database) NOTE: NO
prisma/directory in the plugin. Models are added topackages/database/prisma/schema.prismawith@@schema("plugin_<name>"). NOTE: NOpostcss.config.js— PostCSS is in the shared Vite config. -
Database model requirements (models go in
packages/database/prisma/schema.prisma):- Every model MUST have
@@schema("plugin_[name]")annotation - Every model MUST be prefixed (e.g.,
FeedbackItem, notItem) - Every enum MUST also have
@@schema("plugin_[name]") - Define all models with proper relations, indexes, and timestamps
- Use meaningful field names and types
- Add
createdAtandupdatedAtto every model
- Every model MUST have
-
Backend API requirements:
- RESTful routes under /api/v1/[plugin-name]/
- Proper error handling with try/catch and status codes
- Input validation on POST/PUT routes
- GET routes support pagination (page, limit query params)
- Health check endpoint at /health
-
Frontend requirements:
- Use Tailwind CSS for professional styling
- Use usePluginApi() for all API calls in React components
- For non-React API URLs, use getPluginBackendUrl() — NEVER hardcode localhost:port
- Use useAuth() for user context
- Use useNotify() for success/error feedback
- Include loading skeletons, error states, and empty states
- Responsive design (mobile + desktop)
- Include a form for creating/editing records
- Include a list/table view with sorting and filtering
Quality Standards#
- Full TypeScript — no
anytypes - Prisma singleton pattern for database client
- Modular components and route handlers
- Helpful code comments explaining logic
- Proper error handling everywhere
- Handle edge cases (empty data, network errors, validation errors)
Generate ALL files with complete, working code. No placeholders.
| 1 | |
| 2 | ## How to Customize |
| 3 | |
| 4 | Before pasting, replace: |
| 5 | |
| 6 | 1. **`[YOUR_PLUGIN_NAME]`** — your plugin's name in kebab-case (e.g., `task-tracker`) |
| 7 | 2. **`[DESCRIBE WHAT YOUR PLUGIN SHOULD DO]`** — write 3-5 sentences about the features |
| 8 | 3. **`[Your Name]`** — your name |
| 9 | 4. **`4XX`** — pick a unique port number (e.g., 4050, 4060) |
| 10 | |
| 11 | ## After the AI Generates Code |
| 12 | |
| 13 | ```bash |
| 14 | # 1. Scaffold the plugin |
| 15 | naap-plugin create my-plugin --template full-stack |
| 16 | |
| 17 | # 2. Copy AI-generated model definitions into packages/database/prisma/schema.prisma |
| 18 | # (Remember: @@schema("plugin_my_plugin") on every model and enum) |
| 19 | |
| 20 | # 3. Register the schema (if new) |
| 21 | # Add to docker/init-schemas.sql: CREATE SCHEMA IF NOT EXISTS plugin_my_plugin; |
| 22 | |
| 23 | # 4. Generate and push the unified schema |
| 24 | cd packages/database |
| 25 | npx prisma generate |
| 26 | npx prisma db push |
| 27 | |
| 28 | # 5. Install plugin dependencies |
| 29 | cd ../../plugins/my-plugin/frontend && pnpm install |
| 30 | cd ../backend && pnpm install |
| 31 | |
| 32 | # 6. Start everything |
| 33 | cd ../../.. |
| 34 | naap-plugin dev |
Key: Models live in
packages/database/, not in the plugin. See Database Architecture.
Example: Feedback Board#
Here's a filled-in requirements section:
| 1 | Create a full-stack NaaP plugin called "feedback-board" that does: |
| 2 | |
| 3 | - Users can submit feature requests with a title, description, and category |
| 4 | - Other users can upvote/downvote feedback items |
| 5 | - Items can be filtered by category (Bug, Feature, Improvement) and sorted |
| 6 | by votes, newest, or oldest |
| 7 | - Each item shows the author's name, vote count, and creation date |
| 8 | - Admins (users with "admin" role) can change the status of items: |
| 9 | Under Review, Planned, In Progress, Completed, Declined |
| 10 | - A dashboard shows stats: total items, items by status, top voted items |
| 11 | - Users can comment on feedback items |
Follow-Up Prompts#
| 1 | # Add real-time updates |
| 2 | When someone votes on a feedback item or changes its status, other users |
| 3 | should see the update without refreshing the page. Use the shell's event |
| 4 | bus: useEvents().emit('feedback:updated', { id }) and listen on mount. |
| 1 | # Add email notifications |
| 2 | When an admin changes the status of a feedback item, notify the original |
| 3 | author. Add a notifications table in Prisma and a /notifications endpoint. |
| 4 | Show unread notification count in the plugin header. |
| 1 | # Add analytics dashboard |
| 2 | Create an analytics tab that shows: |
| 3 | - Feedback items over time (line chart) |
| 4 | - Items by category (pie chart) |
| 5 | - Average votes per category (bar chart) |
| 6 | Use recharts or chart.js for the visualizations. |