VelaVela
CRUD

CRUD

Native CRUD resources with named routes, guards, and OpenAPI.

@velajs/crud (>= 1.18) is Vela's native CRUD engine. @Crud() stamps real controller routes — named routes (urlFor), guards, pipes, interceptors, and OpenAPI all flow through Vela's ordinary pipeline. Data access goes through a pluggable adapter (@velajs/crud-memory for tests and prototypes, @velajs/crud-drizzle for SQL).

Installation

npm install @velajs/crud @velajs/crud-memory

Define a model, decorate a controller

import { z } from 'zod';
import { Controller } from '@velajs/vela';
import { Crud, defineModel } from '@velajs/crud';
import { memoryAdapter } from '@velajs/crud-memory';

const Spirit = defineModel({
  name: 'spirit',
  tableName: 'spirits',
  schema: z.object({
    id: z.uuid(),
    name: z.string().min(1),
    element: z.enum(['wind', 'water', 'ember']),
    createdAt: z.number().optional(),
    updatedAt: z.number().optional(),
    deletedAt: z.number().nullable().optional(),
  }),
  softDelete: true,
});

@Controller('/spirits')
@Crud({
  model: Spirit,
  adapter: memoryAdapter({ tableName: 'spirits', softDeleteField: 'deletedAt' }),
})
class SpiritsController {}

This generates (among others — 22 verbs are available):

MethodPathRoute nameOperation
POST/spiritsspirit.createCreate
GET/spiritsspirit.listList
GET/spirits/:idspirit.readRead
PATCH/spirits/:idspirit.updateUpdate
DELETE/spirits/:idspirit.deleteDelete
POST/spirits/:id/restorespirit.restoreRestore
POST/spirits/upsertspirit.upsertUpsert

Route names feed UrlGeneratorService.urlFor, vela route list, and stable OpenAPI operationIds (createSpirit, listSpirits, ...). Request bodies are validated against DTOs derived from the model schema; responses use the canonical envelope ({ success, result[, result_info] }).

Headless resources with CrudModule

No controller class needed — mount resources from a module:

import { Module } from '@velajs/vela';
import { CrudModule } from '@velajs/crud';
import { drizzleAdapter } from '@velajs/crud-drizzle';

@Module({
  imports: [
    CrudModule.forRoot({
      adapter: drizzleAdapter({ db, dialect: 'sqlite', table: spirits }),
    }),
    CrudModule.forFeature([
      { path: '/spirits', model: Spirit },
      { path: '/shrines', model: Shrine, only: ['create', 'list', 'read'] },
    ]),
  ],
})
class AppModule {}

forRoot provides the app-wide default adapter (and optional versioningStore/auditStore); each forFeature resource may override it.

Edit on GitHub

Last updated on

On this page