Skip to main content
Entity is a core component of Sonamu projects, defining both database tables and TypeScript types together.

What is an Entity?

An Entity is a data model definition that includes:
  • Database Schema - Table structure, columns, indexes
  • TypeScript Types - Type definitions for type safety
  • Relations - Connections with other Entities
  • Subsets - Field combinations for API responses
  • Enums - Enumeration types and labels

How to Define an Entity

It’s recommended to visually define Entities in Sonamu UI (http://localhost:1028/sonamu-ui). Sonamu UI provides auto-completion, validation, and real-time preview features.

entity.json File Structure

Entities are saved as entity.json files with the following structure:
{
  "id": "User",
  "table": "users", 
  "title": "User",
  "props": [...],
  "indexes": [...],
  "subsets": {...},
  "enums": {...}
}
File Location: api/src/application/{entity}/{entity}.entity.json

Required Fields

id

The unique identifier for the Entity. Written in PascalCase.
{
  "id": "User"
}
  • Used for Entity class names and type names
  • Example: UserModel, User, UserBaseSchema

table

The database table name. Written in snake_case.
{
  "table": "users"
}
If the table name is omitted, it’s auto-generated based on the Entity ID:
  • Userusers
  • BlogPostblog_posts

title

The display name for the Entity.
{
  "title": "User"
}

props

An array of Entity properties (columns). Each property defines its type and options.
{
  "props": [
    { "name": "id", "type": "integer", "desc": "ID" },
    { "name": "email", "type": "string", "length": 255, "desc": "Email" },
    { "name": "created_at", "type": "date", "dbDefault": "CURRENT_TIMESTAMP" }
  ]
}
Basic Property Options:
OptionTypeDescriptionDefault
namestringProperty name (snake_case)required
typestringData typerequired
descstringDescription-
nullablebooleanAllow NULLfalse
dbDefaultstringDB default value-
Learn More

Optional Fields

parentId

Used when inheriting from another Entity.
{
  "id": "Admin",
  "parentId": "User",
  "title": "Administrator"
}
Using parentId inherits all props from the parent Entity. Use carefully.

indexes

Defines database indexes.
{
  "indexes": [
    {
      "type": "unique",
      "name": "users_email_unique",
      "columns": [{ "name": "email" }]
    },
    {
      "type": "index", 
      "name": "users_created_at_idx",
      "columns": [{ "name": "created_at" }]
    }
  ]
}
Index Types:
  • index - Regular index (improves search performance)
  • unique - Unique index (prevents duplicates)
  • hnsw - HNSW index for Vector search
  • ivfflat - IVFFlat index for Vector search

subsets

Defines field combinations for API responses.
{
  "subsets": {
    "A": ["id", "email", "username", "created_at"],
    "P": ["id", "email", "username", "role"],
    "SS": ["id", "email"]
  }
}
Subset Usage:
async findAll(): Promise<UserSubsetA[]> {
  return this.puri()
    .select<UserSubsetA>("A")
    .many();
}
Including Relation Fields:
{
  "subsets": {
    "P": [
      "id",
      "username",
      "employee.id",
      "employee.department.name"
    ]
  }
}
Using dot notation in Subsets, you can include fields from related Entities. Sonamu automatically generates JOINs.

enums

Defines enumeration types and labels.
{
  "enums": {
    "UserRole": {
      "normal": "Normal",
      "admin": "Administrator"
    },
    "UserOrderBy": {
      "id-desc": "ID Newest First",
      "created_at-desc": "Created At Newest First"
    },
    "UserSearchField": {
      "email": "Email",
      "username": "Name"
    }
  }
}
Enum Usage:
// Auto-generated Zod schema and TypeScript type
import { UserRole } from "./user.types";

// "normal" | "admin"
type Role = z.infer<typeof UserRole>;

// Usage in API
async updateRole(userId: number, role: Role) {
  // ...
}

Practical Examples

Basic Entity Example

user.entity.json
{
  "id": "User",
  "table": "users",
  "title": "User",
  "props": [
    { "name": "id", "type": "integer", "desc": "ID" },
    { "name": "created_at", "type": "date", "desc": "Created At", "dbDefault": "CURRENT_TIMESTAMP" },
    { "name": "email", "type": "string", "length": 255, "desc": "Email" },
    { "name": "username", "type": "string", "length": 255, "desc": "Name" },
    { "name": "password", "type": "string", "length": 255, "desc": "Password" },
    { "name": "birth_date", "type": "date", "nullable": true, "desc": "Birth Date" },
    { "name": "role", "type": "enum", "id": "UserRole", "desc": "Role" },
    { "name": "is_verified", "type": "boolean", "desc": "Verified", "dbDefault": "false" },
    { "name": "deleted_at", "type": "date", "nullable": true, "desc": "Deleted At" }
  ],
  "indexes": [
    { "type": "unique", "name": "users_email_unique", "columns": [{ "name": "email" }] }
  ],
  "subsets": {
    "A": ["id", "email", "username", "role", "created_at"],
    "P": ["id", "email", "username", "role"]
  },
  "enums": {
    "UserRole": { "normal": "Normal", "admin": "Administrator" },
    "UserOrderBy": { "id-desc": "ID Newest First" }
  }
}

Example with Relations

employee.entity.json
{
  "id": "Employee",
  "table": "employees",
  "title": "Employee",
  "props": [
    { "name": "id", "type": "integer", "desc": "ID" },
    { "name": "created_at", "type": "date", "desc": "Created At", "dbDefault": "CURRENT_TIMESTAMP" },
    {
      "type": "relation",
      "name": "user",
      "with": "User",
      "desc": "User",
      "relationType": "OneToOne",
      "hasJoinColumn": true,
      "onDelete": "CASCADE"
    },
    {
      "type": "relation",
      "name": "department",
      "with": "Department",
      "nullable": true,
      "desc": "Department",
      "relationType": "BelongsToOne",
      "onDelete": "SET NULL"
    },
    { "name": "employee_number", "type": "string", "length": 32, "desc": "Employee Number" },
    { "name": "salary", "type": "numeric", "precision": 10, "scale": 2, "nullable": true, "desc": "Salary" },
    { "name": "hire_date", "type": "date", "nullable": true, "desc": "Hire Date" }
  ],
  "indexes": [
    {
      "type": "unique",
      "name": "employees_user_id_unique",
      "columns": [{ "name": "user_id" }]
    }
  ],
  "subsets": {
    "A": [
      "id",
      "created_at",
      "user.username",
      "department.name",
      "employee_number",
      "salary",
      "hire_date"
    ]
  },
  "enums": {
    "EmployeeOrderBy": { "id-desc": "ID Newest First" }
  }
}

Defining Entities in Sonamu UI

  1. Access Sonamu UI
    # After running API server
    http://localhost:1028/sonamu-ui
    
  2. Create Entity
    • Click “Entities” tab
    • Click “Create Entity” button
    • Enter Entity ID, table name, Title
  3. Add Properties (Props)
    • Click “Add Property” button to add new properties
    • Select type and configure options
    • Change order with drag and drop
  4. Define Subsets
    • Add subset keys in “Subsets” tab
    • Select fields to include with checkboxes
    • Relation fields expand as a tree structure
  5. Save and Generate
    • Click “Save” button
    • Entity file auto-generated
    • Migration auto-generated
Learn More

What Gets Auto-Generated After Entity Definition

When you define and save an Entity, Sonamu automatically generates the following:

TypeScript Types

Types and Zod schemas generated in {entity}.types.ts file

Database Migration

Migration file generated with table creation SQL

Base Schemas

Base schemas and Enums added to sonamu.generated.ts

Model Scaffold

{entity}.model.ts template generated (when selected)

Cautions

Be Careful When Modifying Entity Definitions
  • Be cautious when deleting columns or changing types on deployed tables
  • Recommend making changes gradually through migrations
  • Consider referential integrity when changing Relations
Entity Design Tips
  • Always start IDs with integer type
  • Include created_at, updated_at fields
  • Explicitly specify indexes for fields requiring unique constraints
  • Add indexes to frequently searched fields

Next Steps

After completing Entity definition, learn the following topics: