BaasixBaasix

Schema Reference Guide

Note: API-style reference for defining schemas. For a conceptual overview, patterns, and best practices, see Database Schema & Relationships Guide.

← Back to Documentation Home

Table of Contents

  1. Introduction
  2. Schema Structure
  3. Field Types
  4. Field Properties
  5. Relationships
  6. Schema Properties
  7. Indexing
  8. Default Values
  9. Examples
  10. Best Practices

Introduction

In BAASIX, schemas define the structure of your data collections. A schema specifies the fields, their types, relationships between collections, and other properties that determine how data is stored and validated.

Schema Structure

A schema definition contains the following key components:

{
  "collectionName": "products",
  "schema": {
    "name": "Product",
    "timestamps": true,
    "paranoid": true,
    "freezeTableName": true,
    "fields": {
      // Field definitions go here
    },
    "indexes": [
      // Index definitions go here
    ]
  }
}
PropertyTypeDescription
collectionNamestringName of the database collection/table
schemaobjectObject containing the schema definition
schema.namestringDisplay name of the schema
schema.timestampsbooleanAdd createdAt and updatedAt fields automatically (default: true)
schema.paranoidbooleanEnable soft deletes with deletedAt field (default: false)
schema.freezeTableNamebooleanPrevent pluralization of table name (default: false)
schema.fieldsobjectObject containing field definitions
schema.indexesarrayArray of index definitions

Field Types

BAASIX supports the following field types. For a complete guide on filtering these types, see Complete Filter Reference.

Supported Field Type Categories:

  • String Types: String, Text, CiText, TOKEN
  • Numeric Types: Integer, BigInt, Real, Double, Decimal
  • Date/Time Types: DateTime, DateTime_NO_TZ, Date, Time, Time_NO_TZ
  • Other Basic Types: Boolean, JSON, JSONB, UUID, ENUM
  • Array Types: Array_Integer, Array_String, Array_Double, Array_Decimal, Array_DateTime, Array_DateTime_NO_TZ, Array_Date, Array_Time, Array_Time_NO_TZ, Array_UUID, Array_Boolean
  • Range Types: Range_Integer, Range_DateTime, Range_DateTime_NO_TZ, Range_Date, Range_Decimal, Range_Double, Range_Time, Range_Time_NO_TZ
  • PostGIS Types: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, Geography
  • Computed Types: VIRTUAL

String Types

TypeDescriptionExample ValueConfiguration
StringVariable-length string (default 255)"Hello world"values.stringLength for custom length
TextUnlimited-length text"Long paragraph of text..."-
TEXTAlias for Text"Long paragraph of text..."-
CiTextCase-insensitive text (requires citext extension)"hello" matches "HELLO"-
TOKEN9-character unique string token"ABC123XYZ"Auto-generated

Numeric Types

TypeDescriptionExample ValueConfiguration
Integer32-bit signed integer42-
BigInt64-bit signed integer9007199254740991-
RealSingle-precision floating-point3.14-
DoubleDouble-precision floating-point3.141592653589793-
DOUBLEAlias for Double3.141592653589793-
DecimalArbitrary precision decimal123.45values.precision, values.scale

Date/Time Types

Timezone Pattern: Base types include timezone; append _NO_TZ for types without timezone.

TypePostgreSQL TypeDescriptionExample Value
DateTimetimestamp with time zoneTimestamp WITH timezone"2023-06-01T14:30:00Z"
DateTime_NO_TZtimestampTimestamp WITHOUT timezone"2023-06-01T14:30:00"
DatedateDate only (no time)"2023-06-01"
Timetime with time zoneTime WITH timezone"14:30:00+05:30"
Time_NO_TZtimeTime WITHOUT timezone"14:30:00"

Other Basic Types

TypeDescriptionExample Value
BooleanTrue/false valuetrue
JSONStandard JSON type{"key": "value"}
JSONBBinary JSON (more efficient){"key": "value"}
UUIDUniversally unique identifier"550e8400-e29b-41d4-a716-446655440000"
ENUMPredefined list of values"active" (stored as varchar)

Array Types (PostgreSQL Arrays)

For filtering array fields, see Complete Filter Reference - Array Operators.

TypeDescriptionExample Value
Array_IntegerArray of integers[1, 2, 3, 4, 5]
Array_StringArray of strings/text["red", "green", "blue"]
Array_DoubleArray of doubles[1.5, 2.7, 3.9]
Array_DecimalArray of decimals[10.50, 20.75, 30.25]
Array_DateTimeArray of timestamps WITH TZ["2023-01-01T00:00:00Z", ...]
Array_DateTime_NO_TZArray of timestamps WITHOUT TZ["2023-01-01T00:00:00", ...]
Array_DateArray of dates (date only)["2023-01-01", "2023-02-01"]
Array_TimeArray of times WITH TZ["14:30:00+05:30", ...]
Array_Time_NO_TZArray of times WITHOUT TZ["14:30:00", "15:45:00"]
Array_UUIDArray of UUIDs["uuid1", "uuid2"]
Array_BooleanArray of booleans[true, false, true]

Range Types (PostgreSQL Ranges)

Range types store lower/upper bounds with inclusivity flags. For filtering range fields, see Complete Filter Reference.

TypePostgreSQL TypeDescriptionExample Value
Range_Integerint4rangeInteger range{"lower": 1, "upper": 10}
Range_DateTimetstzrangeTimestamp range WITH timezone{"lower": "2023-01-01T00:00:00Z", ...}
Range_DateTime_NO_TZtsrangeTimestamp range WITHOUT timezone{"lower": "2023-01-01T00:00:00", ...}
Range_DatedaterangeDate range (date only){"lower": "2023-01-01", "upper": "2023-12-31"}
Range_DecimalnumrangeNumeric/decimal range{"lower": 10.5, "upper": 100.5}
Range_DoublenumrangeDouble precision range{"lower": 1.5, "upper": 99.9}
Range_TimetimetzrangeTime range WITH timezone{"lower": "09:00:00+05:30", ...}
Range_Time_NO_TZtimerangeTime range WITHOUT timezone{"lower": "09:00:00", "upper": "17:00:00"}

Range Value Structure:

{
  "lower": "value", // Lower bound
  "upper": "value", // Upper bound
  "lowerInclusive": true, // Include lower bound (default: true)
  "upperInclusive": true // Include upper bound (default: true)
}

PostGIS Geometry Types (requires PostGIS extension)

For geospatial filtering (within, contains, intersects, dwithin), see Complete Filter Reference - Geospatial Operators.

TypeDescriptionExample Value
PointSingle point in 2D space{"type": "Point", "coordinates": [lng, lat]}
LineStringLine in 2D space{"type": "LineString", "coordinates": [[lng1,lat1], [lng2,lat2]]}
PolygonPolygon in 2D space{"type": "Polygon", "coordinates": [[[lng1,lat1], ...]]}
MultiPointMultiple points{"type": "MultiPoint", "coordinates": [[lng1,lat1], ...]}
MultiLineStringMultiple line strings{"type": "MultiLineString", ...}
MultiPolygonMultiple polygons{"type": "MultiPolygon", ...}
GeometryCollectionCollection of mixed geometries{"type": "GeometryCollection", ...}
GeographyGeographic data (Earth spheroid)GeoJSON format

PostGIS Configuration: Use values.srid to set Spatial Reference System ID (default: 4326 for WGS84).

Computed/Virtual Types

TypeDescriptionConfiguration
VIRTUALComputed field with SQL expressionRequires calculated property with SQL expression

Virtual Field Example:

{
  "fullName": {
    "type": "VIRTUAL",
    "calculated": "CONCAT(\"firstName\", ' ', \"lastName\")"
  }
}

Field Type Examples

{
  "fields": {
    "id": {
      "type": "UUID",
      "primaryKey": true,
      "defaultValue": {
        "type": "UUIDV4"
      }
    },
    "name": {
      "type": "String",
      "allowNull": false,
      "length": 100
    },
    "description": {
      "type": "Text"
    },
    "price": {
      "type": "Decimal",
      "values": {
        "precision": 10,
        "scale": 2
      }
    },
    "status": {
      "type": "Enum",
      "values": ["draft", "published", "archived"],
      "defaultValue": "draft"
    },
    "tags": {
      "type": "Array",
      "itemType": "String"
    }
  }
}

Field Properties

Each field can have various properties that define its behavior:

PropertyTypeDescription
typestringThe data type of the field
allowNullbooleanWhether the field can be null (default: true)
uniquebooleanWhether the field must be unique (default: false)
primaryKeybooleanWhether the field is the primary key (default: false)
autoIncrementbooleanAuto-increment integer field (default: false)
defaultValuevariousDefault value if none is provided
valuesobject/arrayAdditional values for types like Enum or Decimal
lengthnumberMaximum length for String type
commentstringComment/description for the field
validateobjectValidation rules for the field
hiddenbooleanHide this field in API responses (default: false)
immutablebooleanField cannot be updated after creation (default: false)
descriptionstringHuman-readable description of the field

Field Validation

BAASIX provides comprehensive field-level validation to ensure data integrity. Validation rules are applied during create and update operations.

Validation Rules Reference

RuleApplies ToTypeDescription
minInteger, Float, DecimalnumberMinimum value (inclusive)
maxInteger, Float, DecimalnumberMaximum value (inclusive)
isIntIntegerbooleanValidates that value is an integer
notEmptyString, TextbooleanString must not be empty
isEmailStringbooleanValidates email format
isUrlStringbooleanValidates URL format
lenString, Text[min, max]String length range
is / matchesString, Textregex stringPattern matching with regex

Numeric Validation Examples

{
  "fields": {
    "age": {
      "type": "Integer",
      "validate": {
        "min": 0,
        "max": 120
      }
    },
    "price": {
      "type": "Decimal",
      "values": { "precision": 10, "scale": 2 },
      "validate": {
        "min": 0,
        "max": 999999.99
      }
    },
    "quantity": {
      "type": "Integer",
      "validate": {
        "isInt": true,
        "min": 1
      }
    }
  }
}

String Validation Examples

{
  "fields": {
    "email": {
      "type": "String",
      "validate": {
        "isEmail": true,
        "notEmpty": true
      }
    },
    "website": {
      "type": "String",
      "validate": {
        "isUrl": true
      }
    },
    "username": {
      "type": "String",
      "validate": {
        "notEmpty": true,
        "len": [3, 50]
      }
    },
    "zipCode": {
      "type": "String",
      "validate": {
        "matches": "^\\d{5}(-\\d{4})?$"
      }
    },
    "phone": {
      "type": "String",
      "validate": {
        "is": "^\\+?[1-9]\\d{1,14}$"
      }
    }
  }
}

Combined Validation Example

{
  "fields": {
    "shortDescription": {
      "type": "String",
      "validate": {
        "notEmpty": true,
        "len": [10, 255]
      }
    },
    "rating": {
      "type": "Integer",
      "validate": {
        "isInt": true,
        "min": 1,
        "max": 5
      }
    }
  }
}

When validation fails, the API returns a 400 Bad Request with details about which field and validation rule failed.

Relationships

BAASIX supports the following relationship types:

One-to-Many (hasMany/belongsTo)

{
  "fields": {
    "id": {
      "type": "UUID",
      "primaryKey": true
    },
    "name": {
      "type": "String"
    },
    // Foreign key for the belongsTo relationship
    "category_id": {
      "type": "UUID",
      "allowNull": true
    },
    // Relationship definition
    "category": {
      "relType": "BelongsTo",
      "target": "categories",
      "foreignKey": "category_id",
      "as": "category",
      "onDelete": "SET NULL"
    },
    "products": {
      "relType": "HasMany",
      "target": "products",
      "foreignKey": "category_id",
      "as": "products"
    }
  }
}

Many-to-Many (belongsToMany)

{
  "fields": {
    "id": {
      "type": "UUID",
      "primaryKey": true
    },
    "name": {
      "type": "String"
    },
    "tags": {
      "relType": "BelongsToMany",
      "target": "tags",
      "through": "product_tags",
      "foreignKey": "product_id",
      "otherKey": "tag_id",
      "as": "tags"
    }
  }
}

One-to-One (hasOne/belongsTo)

{
  "fields": {
    "id": {
      "type": "UUID",
      "primaryKey": true
    },
    "user_id": {
      "type": "UUID",
      "allowNull": false,
      "unique": true
    },
    "user": {
      "relType": "BelongsTo",
      "target": "baasix_User",
      "foreignKey": "user_id",
      "as": "user"
    },
    "profile": {
      "relType": "HasOne",
      "target": "profiles",
      "foreignKey": "user_id",
      "as": "profile"
    }
  }
}

Relationship Properties

PropertyTypeDescription
relTypestringType of relationship (BelongsTo, HasMany, etc.)
targetstringTarget collection name
foreignKeystringForeign key field name
otherKeystringOther key in many-to-many relationships
throughstringJunction table for many-to-many relationships
asstringAlias for the relationship
onDeletestringAction on delete (CASCADE, SET NULL, etc.)
onUpdatestringAction on update (CASCADE, RESTRICT, etc.)

Schema Properties

Additional properties you can set at the schema level:

Timestamps

When timestamps: true is set, the following fields are automatically added:

{
  "createdAt": {
    "type": "DateTime",
    "allowNull": false
  },
  "updatedAt": {
    "type": "DateTime",
    "allowNull": false
  }
}

Paranoid (Soft Delete)

When paranoid: true is set, the following field is added:

{
  "deletedAt": {
    "type": "DateTime",
    "allowNull": true
  }
}

Records with a non-null deletedAt value are excluded from normal queries.

Junction Table

The isJunction: true property is automatically set on junction tables created for M2M and M2A relationships:

{
  "name": "posts_tags_tags_junction",
  "isJunction": true,
  "fields": {
    "id": { "type": "Integer", "primaryKey": true },
    "posts_id": { "type": "UUID", "allowNull": false },
    "tags_id": { "type": "UUID", "allowNull": false }
  }
}

Note: Junction tables are automatically created when you define M2M or M2A relationships. You don't need to create them manually. Use the through property to specify a custom junction table name.

Indexing

Define indexes to optimize query performance.

Auto-Indexing for Foreign Keys

BAASIX automatically creates indexes on foreign key columns when you create relationships:

  • M2O/O2O: Index on the FK column (e.g., author_Id)
  • M2M/M2A: Indexes on both FK columns in junction tables

This improves JOIN and filter performance without manual configuration.

Manual Index Definition

{
  "schema": {
    "fields": {
      // Field definitions
    },
    "indexes": [
      {
        "name": "products_name_idx",
        "fields": ["name"]
      },
      {
        "name": "products_category_status_idx",
        "fields": ["category_id", "status"]
      },
      {
        "name": "products_price_idx",
        "fields": [["price", "DESC"]]
      },
      {
        "name": "products_description_fulltext",
        "type": "FULLTEXT",
        "fields": ["description"]
      },
      {
        "name": "products_created_at_idx",
        "method": "BTREE",
        "fields": ["createdAt"]
      },
      {
        "name": "products_location_geo_idx",
        "type": "SPATIAL",
        "fields": ["location"]
      }
    ]
  }
}

Index Properties

PropertyTypeDescription
namestringName of the index
fieldsarrayFields to index
typestringIndex type (BTREE, HASH, GIST, FULLTEXT, SPATIAL, etc.)
uniquebooleanWhether the index enforces uniqueness (default: false)
nullsNotDistinctbooleanWhen true, NULL values are treated as equal in unique indexes (PostgreSQL 15+). Only applies when unique is true.
methodstringIndex method (database-specific)

Default Values

BAASIX supports multiple ways to set default values for fields. Default values are automatically applied when creating new records if no value is provided.

Static Default Values

Simple constant values that are used when no value is provided:

{
  "status": {
    "type": "String",
    "defaultValue": "active"
  },
  "isVerified": {
    "type": "Boolean",
    "defaultValue": false
  },
  "score": {
    "type": "Integer",
    "defaultValue": 0
  },
  "priority": {
    "type": "Integer",
    "defaultValue": 100
  }
}

Dynamic Default Values

Dynamic default values are generated at runtime when a record is created:

{
  "id": {
    "type": "UUID",
    "primaryKey": true,
    "defaultValue": {
      "type": "UUIDV4"
    }
  },
  "shortId": {
    "type": "SUID",
    "defaultValue": {
      "type": "SUID"
    }
  },
  "createdAt": {
    "type": "DateTime",
    "defaultValue": {
      "type": "NOW"
    }
  },
  "orderNumber": {
    "type": "Integer",
    "defaultValue": {
      "type": "AUTOINCREMENT"
    }
  }
}

Default Value Types Reference

TypeDescriptionUse Case
NOWCurrent date and timeTimestamps, createdAt fields
UUIDV4Version 4 UUID (random)Primary keys, unique identifiers
SUIDShort Unique ID (compact, URL-safe)Short IDs, slugs, public identifiers
AUTOINCREMENTAuto-incrementing integerSequential IDs, order numbers
SQLCustom SQL expressionComplex calculations, sequences

SQL Default Values

For advanced use cases, you can use custom SQL expressions:

{
  "sortOrder": {
    "type": "Integer",
    "defaultValue": {
      "type": "SQL",
      "value": "(SELECT COALESCE(MAX(sort_order), 0) + 1 FROM products)"
    }
  },
  "randomCode": {
    "type": "String",
    "defaultValue": {
      "type": "SQL",
      "value": "md5(random()::text)"
    }
  },
  "fiscalYear": {
    "type": "Integer",
    "defaultValue": {
      "type": "SQL",
      "value": "EXTRACT(YEAR FROM CURRENT_DATE)"
    }
  }
}

SQL default values are executed directly on the database. Ensure your SQL expressions are valid for your database system and don't introduce security vulnerabilities.

Complete Default Values Example

{
  "collectionName": "orders",
  "schema": {
    "name": "Order",
    "timestamps": true,
    "fields": {
      "id": {
        "type": "UUID",
        "primaryKey": true,
        "defaultValue": { "type": "UUIDV4" }
      },
      "orderCode": {
        "type": "SUID",
        "unique": true,
        "defaultValue": { "type": "SUID" }
      },
      "orderNumber": {
        "type": "Integer",
        "defaultValue": { "type": "AUTOINCREMENT" }
      },
      "status": {
        "type": "String",
        "defaultValue": "pending"
      },
      "priority": {
        "type": "Integer",
        "defaultValue": 0
      },
      "isUrgent": {
        "type": "Boolean",
        "defaultValue": false
      },
      "placedAt": {
        "type": "DateTime",
        "defaultValue": { "type": "NOW" }
      }
    }
  }
}

Examples

Complete Product Schema Example

{
  "collectionName": "products",
  "schema": {
    "name": "Product",
    "timestamps": true,
    "paranoid": true,
    "fields": {
      "id": {
        "type": "UUID",
        "primaryKey": true,
        "defaultValue": {
          "type": "UUIDV4"
        },
        "description": "Unique identifier for the product"
      },
      "name": {
        "type": "String",
        "allowNull": false,
        "length": 100,
        "validate": {
          "notEmpty": true
        },
        "description": "Product name"
      },
      "slug": {
        "type": "String",
        "unique": true,
        "allowNull": false,
        "description": "URL-friendly product identifier"
      },
      "description": {
        "type": "Text",
        "description": "Detailed product description"
      },
      "price": {
        "type": "Decimal",
        "allowNull": false,
        "values": {
          "precision": 10,
          "scale": 2
        },
        "validate": {
          "min": 0
        },
        "description": "Product price"
      },
      "status": {
        "type": "Enum",
        "values": ["draft", "active", "archived"],
        "defaultValue": "draft",
        "description": "Current product status"
      },
      "featured": {
        "type": "Boolean",
        "defaultValue": false,
        "description": "Whether the product is featured"
      },
      "inventory": {
        "type": "Integer",
        "defaultValue": 0,
        "validate": {
          "min": 0
        },
        "description": "Current inventory level"
      },
      "metadata": {
        "type": "JSON",
        "description": "Additional product metadata"
      },
      "tags": {
        "type": "Array",
        "itemType": "String",
        "description": "Product tags"
      },
      "category_id": {
        "type": "UUID",
        "allowNull": true,
        "description": "Foreign key for category"
      },
      "category": {
        "relType": "BelongsTo",
        "target": "categories",
        "foreignKey": "category_id",
        "as": "category",
        "onDelete": "SET NULL",
        "description": "Product category"
      },
      "images": {
        "relType": "HasMany",
        "target": "product_images",
        "foreignKey": "product_id",
        "as": "images",
        "description": "Product images"
      }
    },
    "indexes": [
      {
        "name": "products_name_idx",
        "fields": ["name"]
      },
      {
        "name": "products_status_featured_idx",
        "fields": ["status", "featured"]
      },
      {
        "name": "products_price_idx",
        "fields": [["price", "DESC"]]
      },
      {
        "name": "products_description_fulltext",
        "type": "FULLTEXT",
        "fields": ["description"]
      }
    ]
  }
}

User Authentication Schema Example

{
  "collectionName": "users",
  "schema": {
    "name": "User",
    "timestamps": true,
    "fields": {
      "id": {
        "type": "UUID",
        "primaryKey": true,
        "defaultValue": {
          "type": "UUIDV4"
        }
      },
      "email": {
        "type": "String",
        "allowNull": false,
        "unique": true,
        "validate": {
          "isEmail": true
        }
      },
      "password": {
        "type": "String",
        "allowNull": false,
        "hidden": true
      },
      "firstName": {
        "type": "String",
        "allowNull": false
      },
      "lastName": {
        "type": "String",
        "allowNull": false
      },
      "role_id": {
        "type": "UUID",
        "allowNull": false
      },
      "role": {
        "relType": "BelongsTo",
        "target": "baasix_Role",
        "foreignKey": "role_id",
        "as": "role"
      },
      "isActive": {
        "type": "Boolean",
        "defaultValue": true
      },
      "lastLogin": {
        "type": "DateTime",
        "allowNull": true
      },
      "profile": {
        "relType": "HasOne",
        "target": "user_profiles",
        "foreignKey": "user_id",
        "as": "profile"
      }
    }
  }
}

Best Practices

  1. Use UUID Primary Keys: UUIDs help prevent predictable IDs and are more suitable for distributed systems.
  2. Apply Proper Validation: Define validation rules to ensure data integrity.
  3. Include Descriptive Comments: Use the description field to document the purpose of each field.
  4. Consider Indexing Performance: Only create indexes for fields that are frequently queried.
  5. Use Relationships Properly: Define relationships to maintain data consistency and enable efficient querying.
  6. Implement Soft Deletes: Use the paranoid option for collections where data should be preserved even after deletion.
  7. Be Consistent with Naming: Follow a consistent naming pattern for fields and relationships.
  8. Set Default Values: Provide sensible defaults to ensure data consistency.
  9. Add Constraints: Use allowNull, unique, and validation rules to enforce data integrity.
  10. Design with Future Changes in Mind: Create flexible schemas that can accommodate future changes.

← Back to Documentation Home

On this page