Reference

Fields

Field types and options for collection schemas in dnax Framework.

Fields define the shape and validation rules of each document in a collection. Every field has a name and a type, plus optional configuration that controls validation, indexing, default values, and more.

Basic Usage

import { define } from '@dnax/core';

export default define.Collection({
  slug: 'users',
  fields: [
    { name: 'name', type: 'string', required: true },
    { name: 'email', type: 'email', required: true, unique: true },
    { name: 'age', type: 'integer' },
    { name: 'isActive', type: 'boolean', defaultValue: true },
  ],
});

Field Types

TypeValidationDescription
stringJoi.string()Plain text value
numberJoi.number()Floating-point number
integerJoi.number().integer()Integer number
booleanJoi.boolean()true or false
emailJoi.string().email()Valid email address
passwordJoi.string()Automatically hashed with Bun on insert and update
uuidJoi.string().uuid()UUID string
urlJoi.string().uri()Valid URL
dateJoi.date()Date value
datetime-localJoi.date()DateTime value
enumJoi.string().valid(...)Value from a predefined list
arrayJoi.array()Array of values
jsonJoi.object()Arbitrary JSON object
randomJoi.string() or Joi.number()Auto-generated value on insert
relationshipJoi.string() (ObjectId)Reference to another collection
ipv4Joi.string().ip({ version: 'ipv4' })IPv4 address
ipv6Joi.string().ip({ version: 'ipv6' })IPv6 address
geojson.PointGeoJSON Point
geojson.LineStringGeoJSON LineString
geojson.PolygonGeoJSON Polygon

Field Options

{
  name: string;
  type: FieldType;
  description?: string;
  required?: boolean;
  unique?: boolean;
  nullable?: boolean;
  defaultValue?: any;
  validate?: {
    schema: Joi.Schema;
  };
  index?: boolean;
  indexType?: 'text' | 'hashed' | '2dsphere' | '2d';
  indexOptions?: {
    expireAfterSeconds?: number;
    sparse?: boolean;
    unique?: boolean;
  };
  enumOptions?: { ... };
  relation?: { ... };
  randomOptions?: { ... };
}
OptionTypeDescription
namestringField name (required)
typeFieldTypeField type (required)
descriptionstringHuman-readable description
requiredbooleanReject the document if this field is missing
uniquebooleanCreate a unique MongoDB index
nullablebooleanAllow null as a value
defaultValueanyValue applied automatically on insert when absent
validate{ schema: Joi.Schema }Custom Joi validation schema (overrides the default)
indexbooleanCreate a MongoDB index on this field
indexTypestringIndex type: text, hashed, 2dsphere, or 2d
indexOptionsobjectAdditional MongoDB index options

Custom Validation

Each field gets a default Joi schema based on its type. To override it with your own rules, use the validate option. Import v from @dnax/[email protected] — it is a Joi instance.

import { define, v } from '@dnax/core';

export default define.Collection({
  slug: 'users',
  fields: [
    {
      name: 'age',
      type: 'number',
      validate: {
        schema: v.number().min(0).max(120),
      },
    },
  ],
});

When validate.schema is provided, it replaces the auto-generated schema for that field. The required option still applies on top of it.

Examples

String with length constraints:

{
  name: 'username',
  type: 'string',
  required: true,
  validate: {
    schema: v.string().alphanum().min(3).max(30),
  },
}

Email restricted to a domain:

{
  name: 'email',
  type: 'email',
  validate: {
    schema: v.string().email().pattern(/@acme\.com$/),
  },
}

Number within a range:

{
  name: 'rating',
  type: 'number',
  validate: {
    schema: v.number().min(1).max(5),
  },
}

Array with item validation:

{
  name: 'scores',
  type: 'array',
  validate: {
    schema: v.array().items(v.number().min(0).max(100)).max(10),
  },
}

JSON object with specific shape:

{
  name: 'address',
  type: 'json',
  validate: {
    schema: v.object({
      street: v.string().required(),
      city: v.string().required(),
      zip: v.string().pattern(/^\d{5}$/).required(),
    }),
  },
}
v is a full Joi instance. Any Joi method (v.string(), v.number(), v.object(), v.array(), v.alternatives(), etc.) is available.

Password

Fields with type: 'password' are automatically hashed using Bun.password.hashSync() on every insert and update operation. You never store or receive a plain-text password.

{ name: 'password', type: 'password', required: true }
Combine with privateFields in the collection API config to hide the hash from REST responses.
api: {
  privateFields: [/password/],
}

Enum

Use enumOptions to restrict a field to a set of allowed values.

Single value

{
  name: 'role',
  type: 'enum',
  enumOptions: {
    items: ['admin', 'editor', 'viewer'],
  },
}

Multiple values

When multiple is true, the field accepts an array of values from the list.

{
  name: 'tags',
  type: 'enum',
  enumOptions: {
    multiple: true,
    items: ['javascript', 'typescript', 'rust', 'go'],
  },
}

Relationship

Reference documents from another collection. On query, use $include to populate the related data with a $lookup.

One-to-one

{
  name: 'author',
  type: 'relationship',
  relation: {
    to: 'users',
  },
}

One-to-many

{
  name: 'contributors',
  type: 'relationship',
  relation: {
    to: 'users',
    hasMany: true,
  },
}

Custom pipeline

Attach an aggregation pipeline to the $lookup stage:

{
  name: 'author',
  type: 'relationship',
  relation: {
    to: 'users',
    pipeline: [
      { $project: { name: 1, email: 1 } },
    ],
  },
}
When inserting or updating, you can pass either a full object with _id or just the ObjectId string — dnax extracts the id automatically.

Random

Auto-generate a unique value on insert only. The value is not regenerated on update.

{
  name: 'code',
  type: 'random',
  randomOptions: {
    length: 10,
    startWith: 'BL-',
  },
}

Random Options

OptionTypeDefaultDescription
lengthnumberNumber of random characters to generate (required)
useLettersbooleanfalseInclude letters (a-zA-Z)
useNumbersbooleantrueInclude digits (0-9)
includeSymbolsstring''Extra characters to include in the charset
excludeSymbolsstring''Characters to exclude from the charset
startWithstring''Prefix prepended to the generated value
endWithstring''Suffix appended to the generated value
toLowerCasebooleanfalseConvert to lowercase
toUpperCasebooleanfalseConvert to uppercase
toNumberbooleanfalseParse the result as an integer
When unique is set or randomOptions is defined, dnax automatically checks the database for duplicates and regenerates until a unique value is found.

Examples

Numeric order code:

{
  name: 'orderNumber',
  type: 'random',
  randomOptions: {
    length: 8,
    useNumbers: true,
    startWith: 'ORD-',
  },
}

Alphanumeric token:

{
  name: 'inviteCode',
  type: 'random',
  randomOptions: {
    length: 12,
    useLetters: true,
    useNumbers: true,
    toUpperCase: true,
  },
}

Indexing

Control MongoDB indexes directly from the field definition.

{ name: 'email', type: 'email', unique: true }

Setting unique: true automatically creates a unique index. For more control, use index, indexType, and indexOptions:

{
  name: 'location',
  type: 'geojson.Point',
  index: true,
  indexType: '2dsphere',
}

TTL Index

Expire documents automatically:

{
  name: 'sessionExpires',
  type: 'date',
  index: true,
  indexOptions: {
    expireAfterSeconds: 3600,
  },
}

Default Values

The defaultValue is applied on insert when the field is absent from the payload.

{ name: 'role', type: 'string', defaultValue: 'member' }
{ name: 'isActive', type: 'boolean', defaultValue: true }
{ name: 'views', type: 'number', defaultValue: 0 }
createdAt and updatedAt are managed automatically by dnax — you don't need to declare them. createdAt is set on insert, and updatedAt is refreshed on every insert and update.

Full Example

import { define } from '@dnax/core';

export default define.Collection({
  slug: 'products',
  fields: [
    { name: 'name', type: 'string', required: true },
    { name: 'sku', type: 'random', randomOptions: { length: 8, startWith: 'SKU-', useNumbers: true } },
    { name: 'price', type: 'number', required: true },
    { name: 'description', type: 'string' },
    { name: 'email', type: 'email', unique: true },
    { name: 'category', type: 'enum', enumOptions: { items: ['electronics', 'clothing', 'food'] } },
    { name: 'tags', type: 'enum', enumOptions: { multiple: true, items: ['new', 'sale', 'featured'] } },
    { name: 'stock', type: 'integer', defaultValue: 0 },
    { name: 'isActive', type: 'boolean', defaultValue: true },
    { name: 'metadata', type: 'json' },
    { name: 'images', type: 'array' },
    { name: 'brand', type: 'relationship', relation: { to: 'brands' } },
    { name: 'relatedProducts', type: 'relationship', relation: { to: 'products', hasMany: true } },
    { name: 'website', type: 'url' },
    { name: 'location', type: 'geojson.Point', index: true, indexType: '2dsphere' },
  ],
});
Copyright © 2026