A plugin packages procedures, raw routes, and H3 middleware behind one .use() call. Built-in features (.auth(), .admin(), .files(), .mcp(), .openapi()) stay as they are — plugins are the extension point for third-party and app-specific features.
Write a plugin
An OuterPlugin has a unique name and optional hooks:
import type { OuterPlugin } from "@outerjs/server";
export const pingPlugin: OuterPlugin = {
name: "ping",
build(ctx) {
return {
procedures: {
ping: ctx.base.handler(() => "pong"),
},
};
},
};Register it anywhere in the chain before .start() / .build():
const server = await new Outer({ db: pglite() }).schema(v1_0).use(pingPlugin).start();POST /rpc/ping is now mounted. Double-registering the same name throws.
Lifecycle hooks
| Hook | When | Purpose |
|---|---|---|
configure |
At .use() time |
Fail fast on bad config. Store options on the plugin object. |
validate |
At the start of build, after Outer’s built-in checks | Reject the build — for example if .auth() is missing. Pure validation, no side effects. |
build |
After built-in procedures (admin, files) are assembled | Return procedures, routes, and/or middleware to mount. |
const needsAuth: OuterPlugin = {
name: "needs-auth",
validate(ctx) {
if (!ctx.resources.auth) throw new Error("This plugin requires .auth()");
},
build(ctx) {
return {
procedures: {
"secure.ping": ctx.base.handler(() => "pong"),
},
routes: [
{
method: "get",
path: "/plugin-health",
handler: () => ({ ok: true }),
},
],
middleware: [
async (event, next) => {
event.res.headers.set("x-plugin", "needs-auth");
return next();
},
],
};
},
};What build can return
| Field | Shape | Behavior |
|---|---|---|
procedures |
Record<string, AnyProcedure> |
Registered like .procedure() — keys are dot-names ("webhook.receive" → POST /rpc/webhook/receive). |
routes |
{ method, path, handler }[] |
Mounted like .route(), before /rpc/**. |
middleware |
(event, next) => Promise<unknown>[] |
Runs after CORS and rate-limit middleware, before routes. |
build receives a PluginContext:
| Field | Description |
|---|---|
resources |
Read-only snapshot of the resources bag (auth, storage, cors, …). |
base |
The oRPC base builder with the current context type — chain .handler() on it. |
schemas |
Schema versions registered so far. |
name |
The Outer instance name, if any. |
Prefer returning procedures over mutating resources. Routes and middleware from plugins are collected and mounted by Outer.
Next steps
.procedure()— hand-written endpoints without a plugin- API reference — full
OuterPlugintypes