Skip to Content
Documentation

Create plugin

Generate the site-notes VitNode plugin with create-vitnode-app, see where it was registered, and open its first page and API endpoint.

A plugin is where a VitNode feature lives. Its pages, API, database tables, translations and AdminCP screens ship together as one package. In this lesson you generate site-notes, a small plugin for short announcements, and open the page and endpoint it starts with. The rest of the beginner path grows this same plugin.

Before you begin

  • A running app from Setup, created with Turborepo (or as a Monorepo App). The plugin generator refuses to run without a turbo.json at the workspace root.

Generate the plugin

From the workspace root, run the generator with --plugin and the name site-notes:

Create a VitNode plugin
bun create vitnode-app@canary site-notes --plugin

Answer Yes to "Install dependencies?". The generator then installs and builds the plugin and reloads a running dev server. If you answered No, run pnpm install (or your manager's install), then restart pnpm dev.

The name becomes the package name, the plugin id, the first page's URL and the root key of its translations. Use a plain name. The generator drops a scope such as @acme/site-notes and refuses names core already serves, such as admin, api or users.

See what the generator changed

The generator printed one line per file it registered the plugin in:

  + Registered in apps/web/src/vitnode.api.config.ts
  + Registered in apps/web/src/vitnode.config.ts

siteNotesPlugin() is now in the plugins array of vitnode.config.ts (pages, translations, AdminCP navigation) and siteNotesApiPlugin() in vitnode.api.config.ts (API routes, permissions, background work). The app's package.json gained "site-notes": "workspace:*". A Monorepo App gets the same lines for apps/api.

The new package, with the files the next lessons change:

builders.ts
hello.route.ts
hello.module.ts
hello.module.test.ts
en.json
home-page.tsx
config.api.ts
config.tsx
routes.ts
manifest.gen.ts
package.json
  • src/config.api.ts declares the API half; src/config.tsx and src/routes.ts the front-end half.
  • src/api/builders.ts binds the five API builders to the plugin, so API files never spell its id: buildModule and buildRoute for modules and routes, buildQueueTask for queue tasks, defineCacheEntry for cache entries and defineEvent for events.
  • Every build writes src/manifest.gen.ts and types/api-registry.gen.d.ts. Commit them; the second one is what types the plugin's own API calls (see API modules).

Check the result

Open http://localhost:3000/site-notes. The page says Hello from your plugin, and its card reads Your plugin's API answered: Hello from site-notes!. That text came from the plugin's API, which you can also call directly:

curl http://localhost:3000/api/site-notes/hello
{ "message": "Hello from site-notes!" }
The generated Hello from your plugin page at /site-notes, with a card reading Your plugin's API answered: Hello from site-notes!

To check the plugin the way the app loads it, run the validator from the app folder:

apps/web
pnpm vitnode plugin validate site-notes

It ends with 1 plugin valid. See Plugin commands for everything it checks.