Configuration
Configure verbs, filtering, hooks, tenancy, and guards.
The @Crud() / forFeature configuration controls which verbs are generated
and how the resource behaves.
Verb selection
Twenty-two verbs exist (core five, restore/clone/upsert, the batch family +
bulkPatch, search/aggregate/export/import, and four version verbs gated on
versioning: true). Select with only or except — table order and model
gates always apply:
@Crud({
model: Spirit,
adapter,
only: ['list', 'read'], // or: except: ['delete', 'import']
})
@Controller('/spirits')
class SpiritsController {}Querying
@Crud({
model: Spirit,
adapter,
filterFields: ['element', 'name'], // ?element[eq]=wind, 12 operators, fail-closed
filterConfig: { element: ['eq', 'in'] }, // per-field operator allow-list
sortFields: ['name'], // ?sort=name&order=desc
searchFields: ['name'], // inline ?search= + GET /search
pagination: {
defaultPerPage: 20,
maxPerPage: 100,
cursor: { enabled: true, field: 'id' }, // keyset cursors (adapter capability)
},
upsert: { keys: ['name'] }, // conflict target for the upsert family
})Hooks
Hooks are ctx-first; update hooks receive two in-transaction snapshots:
hooks: {
beforeCreate: (ctx, data) => ({ ...data, name: data.name.trim() }),
afterUpdate: (ctx, prior, current) => audit(ctx, prior, current),
afterDelete: (ctx, prior) => notify(prior),
}Multi-tenant
Tenant-scoped models require an upstream resolver and an explicit affirmation —
without it, decoration throws MissingTenantResolverError:
// model: defineModel({ ..., multiTenant: true })
@Crud({ model: Tenanted, adapter, tenantResolverMounted: true })
// mounted upstream of the Vela app:
outer.use('*', multiTenant()); // X-Tenant-ID by default; path/query/jwt/custom
outer.route('/', app.getHonoApp());Guards and overrides
Generated routes run through the normal pipeline, so plain Vela decorators
work — and @Override(verb) replaces one generated handler while keeping its
route name and OpenAPI metadata:
@Controller('/spirits')
@UseGuards(AuthGuard)
@Crud({ model: Spirit, adapter })
class SpiritsController {
@Override('list')
list(@CrudCtx() ctx: CrudRequestContext) {
// your handler; ctx.c is the Hono context, ctx.container the request scope
}
}Envelopes and errors
Success bodies are { success: true, result[, result_info] }; errors are
{ success: false, error: { code, message, details? } }. Both are pluggable:
responseEnvelope: {
success: (result, info) => ({ data: result, meta: info ?? null }),
error: (err) => ({ problem: err.code, detail: err.message }),
}CRUD
Native CRUD resources with named routes, guards, and OpenAPI.
Better Auth
Authentication for Vela apps via better-auth — email/password, social OAuth, magic link, passkeys, 2FA, and organizations, with full DI integration.
Last updated on