Skip to content

Extension points

Register field types, OAuth2 providers, storage drivers, email transports, push providers, hooks and routes from code, and listen to record and sign-up events.

Since v0.19.0, the pieces Vaultbase is built from are registries. The built-in field types, OAuth2 providers, storage drivers, email transports and push providers are registered through them, and code that embeds Vaultbase can add its own. Everything is exported from src/extend.ts; each register* function returns a function that removes what it added.

import { on } from "./src/extend.ts";
on("record.created", ({ collection, record, auth, source }) => {
console.log(`${collection.name}/${record.id} created via ${source}`);
});
EventPayload
record.createdcollection, record, auth, source
record.updatedcollection, record, existing, auth, source
record.deletedcollection, id, existing, auth, source
auth.signupcollection, record, method (password, invite, anonymous, oauth2)

source is api (records API, hook and route helpers, batch), auth (an account created by an auth flow) or cascade (a relation cleared because its target was deleted).

Listeners run in registration order. Record events are emitted after the write has committed, so nothing a listener does can fail the write; a listener that throws is logged and the others still run. Realtime, webhooks and after-hooks are listeners on the same bus.

FunctionAdds
registerFieldTypeA field type: SQLite column type, validation, normalize, encode/decode, OpenAPI schema. Built-in types can’t be replaced.
registerOAuthProviderAn OAuth2 / OIDC sign-in provider.
registerStorageDriverA file storage backend. Select it with the storage.driver setting.
registerEmailTransportAn email transport. Select it with the email.transport setting.
registerPushProviderA push notification provider.
registerHook, registerRoute, registerJob, registerWorkerHooks, custom routes, cron jobs and queue workers defined in code. Admin UI hooks run first; an admin UI route on the same method and path wins.

A new collection field must use a registered type, and storage.driver / email.transport must name a registered driver or transport.

import { registerFieldType } from "./src/extend.ts";
registerFieldType({
type: "money",
sqlType: "INTEGER",
validate: (v) => (Number.isInteger(v) ? null : "must be a whole number of cents"),
});

GET /api/v1/admin/extensions (admin only) lists every registered field type, provider, driver, transport, hook, route, job and worker.

Code registered this way runs in the server process with full privileges, like hooks.