Skip to content

@resourcefulColumn

The @resourcefulColumn decorator enhances AdonisJS Lucid column decorators with validation, access control, and metadata functionality. It provides both a generic decorator and type-specific variants for common data types.

For complete API reference, see resourcefulColumn.

Overview

The @resourcefulColumn decorator combines the functionality of Lucid's @column decorator with resourceful features:

  • Type Validation: Joi schema validation based on the specified ResourcefulDataType
  • Access Control: Field-level read/write permissions
  • Data Transformation: Automatic prepare/consume functions for type safety
  • OpenAPI Integration: Schema generation for API documentation
  • Metadata Storage: Rich metadata for runtime introspection

Basic Usage

Generic Column

typescript
import { resourcefulColumn } from '@nhtio/lucid-resourceful'
import { ResourcefulStringType } from '@nhtio/lucid-resourceful/definitions'

class User extends compose(BaseModel, withResourceful({ name: 'User' })) {
  @resourcefulColumn({ 
    type: ResourcefulStringType({ minLength: 2, maxLength: 100 }),
    nullable: false,
    columnName: 'user_name' // Maps to 'user_name' column in database
  })
  declare name: string
}

Primary Key Column

typescript
import { ResourcefulUnsignedIntegerType } from '@nhtio/lucid-resourceful/definitions'

@resourcefulColumn({ 
  isPrimary: true,
  type: ResourcefulUnsignedIntegerType({ readOnly: true }),
  nullable: false
})
declare id: number

Type-Specific Variants

String Columns

typescript
// Basic string column
@resourcefulColumn.string({ 
  type: ResourcefulStringType({ minLength: 3, maxLength: 255 }),
  nullable: false 
})
declare name: string

// Email column with validation
@resourcefulColumn.string({ 
  type: ResourcefulStringType({ format: 'email' }),
  nullable: false,
  serializeAs: 'email_address'
})
declare email: string

// Optional text field
@resourcefulColumn.string({ 
  type: ResourcefulStringType({ maxLength: 1000 }),
  nullable: true 
})
declare description: string | null

Numeric Columns

typescript
// Regular number
@resourcefulColumn.number({ 
  type: ResourcefulNumberType({ min: 0, max: 999.99 }),
  nullable: false 
})
declare price: number

// Integer
@resourcefulColumn.integer({ 
  type: ResourcefulIntegerType({ min: 18, max: 120 }),
  nullable: false 
})
declare age: number

// Unsigned integer
@resourcefulColumn.unsignedint({ 
  type: ResourcefulUnsignedIntegerType({ min: 1 }),
  nullable: false 
})
declare count: number

// BigInt for large numbers
@resourcefulColumn.bigint({ 
  type: ResourcefulBigintType(),
  nullable: false 
})
declare largeValue: bigint

Boolean Columns

typescript
@resourcefulColumn.boolean({ 
  type: ResourcefulBooleanType(),
  nullable: false,
  default: false
})
declare isActive: boolean

Date and DateTime Columns

typescript
// Date column
@resourcefulColumn.date({ 
  type: ResourcefulDateType(),
  nullable: true
})
declare birthDate: Date | null

// DateTime with auto-timestamps
@resourcefulColumn.dateTime({ 
  type: ResourcefulDateTimeType(),
  autoCreate: true,
  nullable: false
})
declare createdAt: DateTime

@resourcefulColumn.dateTime({ 
  type: ResourcefulDateTimeType(),
  autoCreate: true,
  autoUpdate: true,
  nullable: false
})
declare updatedAt: DateTime

Binary Columns

typescript
@resourcefulColumn.binary({ 
  type: ResourcefulBinaryType(),
  nullable: true
})
declare fileData: Buffer | null

JSON Columns

typescript
// JSON object
@resourcefulColumn.object({ 
  type: ResourcefulObjectType({
    properties: {
      settings: { type: 'object' },
      preferences: { type: 'object' }
    }
  }),
  nullable: true
})
declare metadata: Record<string, any> | null

// JSON array
@resourcefulColumn.array({ 
  type: ResourcefulArrayType({ 
    items: { type: 'string' }
  }),
  nullable: true
})
declare tags: string[] | null

Configuration Options

Required Options

type

  • Type: ResourcefulDataType
  • Required: Yes

The ResourcefulDataType instance that defines the field's validation schema and type information. See Data Type Definitions for available types and their configuration options.

typescript
@resourcefulColumn({ 
  type: ResourcefulStringType({ minLength: 1, maxLength: 255 })
})
declare name: string

Lucid Column Options

All standard Lucid column options are supported:

typescript
@resourcefulColumn({
  type: ResourcefulStringType(),
  columnName: 'custom_column_name',  // Database column name
  isPrimary: true,                   // Primary key
  serializeAs: 'serialized_name',    // Serialization name
  meta: { custom: 'metadata' }       // Custom metadata
})
declare field: string

Resourceful Options

nullable

  • Type: boolean
  • Default: false

Whether the field can be null.

typescript
@resourcefulColumn.string({ 
  type: ResourcefulStringType(),
  nullable: true 
})
declare optionalField: string | null

description

  • Type: string
  • Optional: Yes

Description for OpenAPI documentation.

typescript
@resourcefulColumn.string({ 
  type: ResourcefulStringType(),
  description: 'The user\'s full name'
})
declare name: string

default

  • Type: any
  • Optional: Yes

Default value for the field.

typescript
@resourcefulColumn.boolean({ 
  type: ResourcefulBooleanType(),
  default: false
})
declare isActive: boolean

deprecated

  • Type: boolean
  • Optional: Yes

Mark the field as deprecated in OpenAPI documentation.

typescript
@resourcefulColumn.string({ 
  type: ResourcefulStringType(),
  deprecated: true
})
declare oldField: string

example

  • Type: string
  • Optional: Yes

Example value for OpenAPI documentation.

typescript
@resourcefulColumn.string({ 
  type: ResourcefulStringType(),
  example: 'john.doe@example.com'
})
declare email: string

externalDocs

  • Type: ExternalDocumentationObject
  • Optional: Yes

External documentation reference.

typescript
@resourcefulColumn.string({ 
  type: ResourcefulStringType(),
  externalDocs: {
    description: 'Email format specification',
    url: 'https://tools.ietf.org/html/rfc5322'
  }
})
declare email: string

Access Control

Read Access Control

Control who can read the field value:

typescript
@resourcefulColumn.string({
  type: ResourcefulStringType(),
  readAccessControlFilters: [
    // Only the user themselves can read their email
    (ctx, app, instance) => ctx.auth.user?.id === instance?.id,
    // Or admins can read any email
    (ctx) => ctx.auth.user?.role === 'admin'
  ]
})
declare email: string

Write Access Control

Control who can modify the field value:

typescript
@resourcefulColumn.string({
  type: ResourcefulStringType(),
  writeAccessControlFilters: [
    // Only admins can modify user roles
    (ctx) => ctx.auth.user?.role === 'admin'
  ]
})
declare role: string

Combined Access Control

typescript
@resourcefulColumn.string({
  type: ResourcefulStringType(),
  readAccessControlFilters: [
    (ctx, app, instance) => ctx.auth.user?.id === instance?.id || ctx.auth.user?.role === 'admin'
  ],
  writeAccessControlFilters: [
    (ctx, app, instance) => ctx.auth.user?.id === instance?.id
  ]
})
declare personalNote: string

Validation Scoping

Dynamically modify validation based on context:

typescript
@resourcefulColumn.string({
  type: ResourcefulStringType(),
  validationScoper: (schema, ctx, operation) => {
    if (operation === 'create') {
      // Require minimum length on creation
      return schema.min(3)
    }
    if (ctx.auth.user?.role === 'admin') {
      // Admins can use longer strings
      return schema.max(500)
    }
    return schema.max(100)
  }
})
declare title: string

Date Column Specific Options

Auto-timestamps

Date and DateTime columns support automatic timestamp management:

typescript
@resourcefulColumn.dateTime({ 
  type: ResourcefulDateTimeType(),
  autoCreate: true,     // Set on creation
  autoUpdate: false,    // Don't update on save
  nullable: false
})
declare createdAt: DateTime

@resourcefulColumn.dateTime({ 
  type: ResourcefulDateTimeType(),
  autoCreate: true,     // Set on creation
  autoUpdate: true,     // Update on every save
  nullable: false
})
declare updatedAt: DateTime

Complete Example

typescript
import { DateTime } from 'luxon'
import { BaseModel } from '@adonisjs/lucid/orm'
import { compose } from '@adonisjs/core/helpers'
import { withResourceful, resourcefulColumn } from '@nhtio/lucid-resourceful'
import { 
  ResourcefulUnsignedIntegerType,
  ResourcefulStringType,
  ResourcefulBooleanType,
  ResourcefulDateTimeType,
  ResourcefulObjectType
} from '@nhtio/lucid-resourceful/definitions'

export default class User extends compose(BaseModel, withResourceful({ 
  name: 'User' 
})) {
  @resourcefulColumn({ 
    isPrimary: true,
    type: ResourcefulUnsignedIntegerType({ readOnly: true }),
    nullable: false
  })
  declare id: number

  @resourcefulColumn.string({
    type: ResourcefulStringType({ minLength: 2, maxLength: 100 }),
    nullable: false,
    description: 'User\'s full name',
    example: 'John Doe'
  })
  declare name: string

  @resourcefulColumn.string({
    type: ResourcefulStringType({ format: 'email' }),
    nullable: false,
    description: 'User\'s email address',
    readAccessControlFilters: [
      (ctx, app, user) => ctx.auth.user?.id === user?.id || ctx.auth.user?.role === 'admin'
    ],
    writeAccessControlFilters: [
      (ctx, app, user) => ctx.auth.user?.id === user?.id
    ]
  })
  declare email: string

  @resourcefulColumn.boolean({
    type: ResourcefulBooleanType(),
    nullable: false,
    default: true,
    writeAccessControlFilters: [
      (ctx) => ctx.auth.user?.role === 'admin'
    ]
  })
  declare isActive: boolean

  @resourcefulColumn.object({
    type: ResourcefulObjectType(),
    nullable: true,
    description: 'User preferences and settings',
    readAccessControlFilters: [
      (ctx, app, user) => ctx.auth.user?.id === user?.id
    ]
  })
  declare preferences: Record<string, any> | null

  @resourcefulColumn.dateTime({ 
    type: ResourcefulDateTimeType(),
    autoCreate: true,
    nullable: false
  })
  declare createdAt: DateTime

  @resourcefulColumn.dateTime({ 
    type: ResourcefulDateTimeType(),
    autoCreate: true,
    autoUpdate: true,
    nullable: false
  })
  declare updatedAt: DateTime
}

Common Patterns

Audit Fields

typescript
@resourcefulColumn.string({
  type: ResourcefulStringType(),
  nullable: true,
  readAccessControlFilters: [(ctx) => ctx.auth.user?.role === 'admin']
})
declare createdBy: string | null

@resourcefulColumn.string({
  type: ResourcefulStringType(),
  nullable: true,
  readAccessControlFilters: [(ctx) => ctx.auth.user?.role === 'admin']
})
declare updatedBy: string | null

Soft Deletes

typescript
@resourcefulColumn.dateTime({
  type: ResourcefulDateTimeType(),
  nullable: true,
  serializeAs: null, // Don't serialize in responses
  writeAccessControlFilters: [(ctx) => ctx.auth.user?.role === 'admin']
})
declare deletedAt: DateTime | null

Multi-tenant Fields

typescript
@resourcefulColumn.unsignedint({
  type: ResourcefulUnsignedIntegerType(),
  nullable: false,
  serializeAs: null, // Don't expose in API
  writeAccessControlFilters: [() => false] // Never allow direct writes
})
declare tenantId: number

Best Practices

  1. Always specify nullable: Be explicit about whether fields can be null
  2. Use appropriate types: Choose the most specific ResourcefulDataType for your use case
  3. Secure sensitive fields: Use access control filters for sensitive information
  4. Provide descriptions: Add descriptions for better API documentation
  5. Use validation scoping: Implement context-aware validation when needed
  6. Consider serialization: Use serializeAs to control API field names
  7. Implement audit trails: Add audit fields for tracking changes