SuperAPI AI Directory

btst-backend-plugin-dev

Coding & development

View repository

Updated 10-16 08:28

About this skill

BTST Backend Plugin Development

File structure

src/plugins/{name}/
  api/
    plugin.ts        ← defineBackendPlugin entry
    getters.ts       ← read-only DB functions (no HTTP context)
    mutations.ts     ← lower-level write DB functions
    operations.ts    ← validated operations, permission facts, lifecycle
    index.ts         ← re-exports getters + mutations + types
  query-keys.ts      ← React Query key factory

Rules

  • getters.ts — pure async DB functions only. No HTTP context, no lifecycle hooks. Always takes adapter as first arg.
  • mutations.ts — lower-level write primitives. No HTTP context or lifecycle composition. Document that callers own validation and lifecycle.
  • api/index.ts — re-export everything from getters + mutations for direct server-side import.
  • operations.ts — define the one maintained business inventory with input validation, exact permission descriptors, authoritative facts, domain execution, and lifecycle hooks.
  • Bind HTTP routes to same-key operations. Use operationRouteMap only for real route-name mismatches.
  • The optional raw factory is narrow: first-party plugins expose only prefetchForRoute for SSG. Do not duplicate business getters or mutations on createBackendStack().raw.
  • Use myStack.forRequest(request).operations.* for request work and myStack.trusted.* for explicitly trusted jobs.

Key patterns

  • Import defineBackendPlugin and createEndpoint from "@btst/stack/plugins/api" (not @btst/stack/plugins).
  • Import the adapter type as import type { DBAdapter as Adapter } from "@btst/db".
  • Routes are defined with createEndpoint(path, { method, query?, body? }, handler) — not string-keyed "GET /path" objects.
  • Route handlers return data directly (return item) — no ctx.json().
  • Throw errors with throw ctx.error(statusCode, { message }).
  • The routes factory returns a named object: return { listItems, createItem } as const.
  • Export the router type as ReturnType<ReturnType<typeof myBackendPlugin>["routes"]>.

Lifecycle hook naming

Pattern: onBefore{Action}{Entity}, onAfter{Action}{Entity}, onError{Action}{Entity}

onBeforeListPosts, onAfterListPosts, onErrorListPosts
onBeforeCreatePost, onAfterCreatePost, onErrorCreatePost
onBeforeUpdatePost, onAfterUpdatePost, onErrorUpdatePost
onBeforeDeletePost, onAfterDeletePost, onErrorDeletePost

// Preserve meaningful domain events instead of inventing CRUD phases.
onBeforeChat, onAfterChat, onErrorChat
onBeforeActivateTools

Normalize names without adding lifecycle phases that the plugin does not already support.

Trusted operations in AI tool execute functions

myStack is a module-level const. The execute closure runs lazily (only on HTTP request), so myStack is always initialised by then:

import { createBackendStack } from "@btst/stack/api"

export const myStack = createBackendStack({ ... })

const myTool = tool({
  execute: async (params) => {
		await myStack.trusted.kanban.createTask({
			title: params.title,
			columnId: "col-id",
		})
    return { success: true }
  }
})

Gotchas

  • Wrong import path — always import from "@btst/stack/plugins/api", not "@btst/stack/plugins".
  • Wrong adapter type — use import type { DBAdapter as Adapter } from "@btst/db" in getters/mutations/plugin files.
  • "GET /path" string keys — routes use createEndpoint(), not string-keyed method/path objects.
  • ctx.json() — does not exist; return data directly from route handlers.
  • Business methods on createBackendStack().raw — do not add them. Keep the composed lifecycle explicit through forRequest(request).operations or trusted, and reserve raw for narrow lower-level/SSG helpers.
  • Authorization in lifecycle hooks — routine access control belongs in operation descriptors and the one shared rule. Hooks receive already-authorized context.
  • Write ops in getters.ts — write functions belong in mutations.ts, not getters.ts.

Full code patterns

See REFERENCE.md for complete defineBackendPlugin, getters, mutations, and api/index.ts code shapes.