Enum is an enumeration type that can only select from a predefined list of values. It provides type safety and makes it easy to configure selection options in user interfaces.
What is an Enum?
Enum defines a limited set of values to provide:
Type Safety Prevents undefined value input Automatically converts to Union type in TypeScript
Auto UI Generation Automatic selection list configuration Immediately usable in Select, ButtonSet components
Label Management Separation of keys and display names Easy multi-language support
Maintainability Centralized management Only one place to modify when values change
Defining Enums
Defining in entity.json
Define as key-label pairs in the Entityβs enums section.
{
"id" : "User" ,
"props" : [
{
"name" : "role" ,
"type" : "enum" ,
"id" : "UserRole" ,
"desc" : "User role"
},
{
"name" : "status" ,
"type" : "enum" ,
"id" : "UserStatus" ,
"desc" : "Account status"
}
],
"enums" : {
"UserRole" : {
"admin" : "Administrator" ,
"normal" : "Normal User" ,
"guest" : "Guest"
},
"UserStatus" : {
"active" : "Active" ,
"inactive" : "Inactive" ,
"suspended" : "Suspended" ,
"deleted" : "Deleted"
}
}
}
Structure :
Enum ID : PascalCase (e.g., UserRole, PostStatus)
Key : lowercase, numbers, underscores (e.g., admin, draft_saved)
Label : Display text (e.g., βAdministratorβ, βDraft Savedβ)
Defining in Sonamu UI
Navigate to Entity edit page
Select an Entity in Sonamu UI.
Scroll to Enums section
The Enums section is below Props, Indexes, Subsets.
Click Add Enum
Add a new Enum. Enter Enum ID :
Enter in PascalCase
Use {Entity} pattern to associate with Entity
Example: $ModelRole β UserRole (auto-converted)
Add Enum values
Each Enum is displayed as a separate tab, add key-label pairs with the βAdd Rowβ button.
Key : lowercase letters, numbers, underscores only
Label : Display name
Save
When you save the Entity, the Enum is saved together.
Auto-Generated Code
When you define an Enum, Sonamu automatically generates the following:
1. Zod Schema
import { z } from "zod" ;
// Enum schema
export const UserRole = z . enum ([ "admin" , "normal" , "guest" ]);
// Enum labels
export const UserRoleLabels = {
admin: "Administrator" ,
normal: "Normal User" ,
guest: "Guest" ,
} as const ;
2. TypeScript Type
import { UserRole } from "./sonamu.generated" ;
// Inferred as Union type
export type UserRole = z . infer < typeof UserRole >;
// Result: "admin" | "normal" | "guest"
3. Label Helper Function
// Get label function
export function getUserRoleLabel ( role : UserRole ) : string {
return UserRoleLabels [ role ];
}
Using Enums
Using in Props
{
"props" : [
{
"name" : "role" ,
"type" : "enum" ,
"id" : "UserRole" ,
"desc" : "User role" ,
"dbDefault" : "normal"
}
]
}
Database :
Column type: text
Stored value: Key value (e.g., "admin")
TypeScript :
type User = {
id : number ;
role : "admin" | "normal" | "guest" ; // Union type
};
Using in API
Using Enum in Model
Validation with Zod
import { UserRole } from "./user.types" ;
export class UserModel extends BaseModel {
@ api ({ httpMethod: "POST" })
async updateRole ( userId : number , role : UserRole ) {
// role is type-safe
await this . puri ()
. where ( "id" , userId )
. update ({ role });
return { success: true };
}
}
Displaying Labels
import { getUserRoleLabel } from "./sonamu.generated" ;
const user = await UserModel . findById ( 1 );
const roleLabel = getUserRoleLabel ( user . role );
// "Administrator"
Common Enum Patterns
1. OrderBy Enum
Define sorting options.
{
"enums" : {
"UserOrderBy" : {
"id-desc" : "ID Newest First" ,
"id-asc" : "ID Oldest First" ,
"created_at-desc" : "Created At Newest First" ,
"created_at-asc" : "Created At Oldest First" ,
"username-asc" : "Name Order"
}
}
}
Usage :
async findAll ( orderBy : UserOrderBy = "id-desc" ) {
const [ field , direction ] = orderBy . split ( "-" );
return this . puri ()
. orderBy ( field , direction as "asc" | "desc" )
. many ();
}
2. SearchField Enum
Define searchable fields.
{
"enums" : {
"UserSearchField" : {
"email" : "Email" ,
"username" : "Name" ,
"phone" : "Phone"
}
}
}
Usage :
async search ( field : UserSearchField , keyword : string ) {
return this . puri ()
. whereLike ( field , `% ${ keyword } %` )
. many ();
}
3. Status Enum
Define statuses.
{
"enums" : {
"PostStatus" : {
"draft" : "Draft" ,
"published" : "Published" ,
"archived" : "Archived" ,
"deleted" : "Deleted"
}
}
}
Usage :
async publish ( postId : number ) {
await this . puri ()
. where ( "id" , postId )
. update ({ status: "published" });
}
4. Type/Category Enum
Define classifications.
{
"enums" : {
"NotificationType" : {
"email" : "Email" ,
"sms" : "SMS" ,
"push" : "Push Notification" ,
"in_app" : "In-App Notification"
}
}
}
Enum Array Type
You can store multiple Enum values as an array.
{
"props" : [
{
"name" : "permissions" ,
"type" : "enum[]" ,
"id" : "Permission" ,
"desc" : "Permission list"
}
],
"enums" : {
"Permission" : {
"read" : "Read" ,
"write" : "Write" ,
"delete" : "Delete" ,
"admin" : "Admin"
}
}
}
Database :
Column type: text[]
Stored value: ["read", "write"]
TypeScript :
type User = {
id : number ;
permissions : ( "read" | "write" | "delete" | "admin" )[];
};
Usage :
// Check permission
function hasPermission ( user : User , permission : Permission ) : boolean {
return user . permissions . includes ( permission );
}
// Grant permission
async grantPermission ( userId : number , permission : Permission ) {
const user = await this . findById ( userId );
if ( ! user . permissions . includes ( permission )) {
await this . puri ()
. where ( "id" , userId )
. update ({
permissions: [ ... user . permissions , permission ]
});
}
}
Using Enums in Frontend
Select Component
Using Generated Component
Manual Implementation
import { UserRoleSelect } from "@/components/UserRoleSelect" ;
function UserForm () {
const [ role , setRole ] = useState < UserRole >( "normal" );
return (
< UserRoleSelect
value = { role }
onChange = { setRole }
/>
);
}
import { UserStatusButtonSet } from "@/components/UserStatusButtonSet" ;
function StatusFilter () {
const [ status , setStatus ] = useState < UserStatus | null >( null );
return (
< UserStatusButtonSet
value = { status }
onChange = { setStatus }
allowNull // Allow deselection
/>
);
}
Enum Validation
Zod automatically validates Enum values.
API Parameter Validation
Custom Validation
import { UserRole } from "./sonamu.generated" ;
export const UpdateRoleParams = z . object ({
userId: z . number (),
role: UserRole , // Auto-validated
});
// When invalid value is input
UpdateRoleParams . parse ({
userId: 1 ,
role: "invalid_role" // Error!
});
Enum Naming Conventions
Recommended Patterns
Purpose Pattern Example Entity status {Entity}StatusPostStatus, OrderStatusEntity type {Entity}TypeNotificationType, PaymentTypeEntity role {Entity}RoleUserRole, MemberRoleSort options {Entity}OrderByUserOrderBy, PostOrderBySearch fields {Entity}SearchFieldUserSearchFieldCategory {Entity}CategoryProductCategoryPermission {Entity}PermissionUserPermission
$Model Pattern
Use $Model to dynamically include the Entity name:
{
"enums" : {
"$ModelRole" : {
"admin" : "Administrator" ,
"normal" : "Normal"
},
"$ModelStatus" : {
"active" : "Active" ,
"inactive" : "Inactive"
}
}
}
Conversion result :
User Entity β UserRole, UserStatus
Post Entity β PostRole, PostStatus
Cautions
Caution When Changing Enum Values Changing an Enum key thatβs already stored in the database will cause mismatch with existing data. How to change :
Add new key
Data migration (old key β new key)
Remove old key
Key Naming Restrictions Enum Keys can only use:
Lowercase letters (a-z)
Numbers (0-9)
Underscores (_)
Hyphens (-) cannot be used.
Labels Can Be Changed Freely Labels are for display purposes, so they can be changed anytime. Only Keys are stored in the database.
Enum vs Separate Table
Criteria Enum Separate Table Number of values Few (< 20) Many (> 20) Change frequency Rarely Frequently Additional info Not needed Needed (description, order, etc.) Performance Fast (no JOIN) Slower (JOIN needed) Recommended use Status, role, type Category, code tables
Enum use examples :
User roles (admin, user, guest)
Post status (draft, published, deleted)
Notification types (email, sms, push)
Separate table use examples :
Region list (Seoul, Busan, β¦)
Product categories (hierarchical structure)
Country codes (many additional info)
Next Steps