PluginsREST API

API Routes

Validate plugin-owned Hono route inputs and responses with Zod, then protect staff actions with a clear permission.

Create the plugin and its API module first. Then put the endpoint in that module so its validation, permission, and OpenAPI record travel together.

Validate input and response

plugins/site-notes/src/api/modules/notes/get.route.ts
import { z } from '@hono/zod-openapi'
import { buildRoute } from '@vitnode/core/api/lib/route'

export const getNoteRoute = buildRoute({
  pluginId: '@acme/site-notes',
  route: {
    method: 'get',
    path: '/{id}',
    request: {
      params: z.object({ id: z.coerce.number().int().positive() }),
    },
    responses: {
      200: {
        content: {
          'application/json': {
            schema: z.object({ id: z.number(), title: z.string() }),
          },
        },
        description: 'One site note.',
      },
    },
  },
  handler: (c) => {
    const { id } = c.req.valid('param')
    return c.json({ id, title: 'Plugin-owned note' })
  },
})

Gate an AdminCP action

Add adminStaffPermission to an action that only staff should call. The API still authorizes server-side; a hidden button is merely good manners.

plugins/site-notes/src/api/modules/notes/publish.route.ts
import { z } from '@hono/zod-openapi'
import { buildRoute } from '@vitnode/core/api/lib/route'

export const publishNoteRoute = buildRoute({
  adminStaffPermission: {
    module: 'site_notes',
    permission: 'can_publish',
  },
  pluginId: '@acme/site-notes',
  route: {
    method: 'post',
    path: '/{id}/publish',
    responses: {
      200: {
        content: {
          'application/json': {
            schema: z.object({ published: z.literal(true) }),
          },
        },
        description: 'The note was published.',
      },
    },
  },
  handler: async (c) => c.json({ published: true }),
})

One route, one plugin owner

Keep handlers with the feature that owns the data. The host API config only composes plugins; it should not become a surprise sequel to your business logic.