Sonamu automatically generates TypeScript types, Zod schemas, API clients, React components, and more from Entity definitions. This document describes the types and purposes of all generated files.
Generated Files Overview
Types & Schemas TypeScript types and Zod schemas *.types.ts, sonamu.generated.ts
API Clients HTTP client functions services.generated.ts
Query Helpers Subset query functions sonamu.generated.sso.ts
React Components Form, List, Select components view_*.tsx
Core Generated Files
1. Entity Types ({entity}.types.ts)
TypeScript types and Zod schemas are generated per Entity.
api/src/application/user/user.types.ts (auto-generated)
import { z } from "zod" ;
// Base type
export type User = {
id : number ;
email : string ;
username : string ;
role : "admin" | "normal" ;
created_at : Date ;
};
// Zod schema
export const User = z . object ({
id: z . number (),
email: z . string (),
username: z . string (),
role: z . enum ([ "admin" , "normal" ]),
created_at: z . date (),
});
// List parameters
export const UserListParams = z . object ({
num: z . number (). optional (),
page: z . number (). optional (),
search: UserSearchField . optional (),
keyword: z . string (). optional (),
orderBy: UserOrderBy . optional (),
});
// Save parameters
export const UserSaveParams = User . partial ({ id: true });
Generated when : Entity save or pnpm sonamu sync
Editable : ❌ Auto-regenerated (custom types in separate file)
2. Generated Base (sonamu.generated.ts)
Base types and Enums for the entire project.
api/src/application/sonamu.generated.ts (auto-generated)
import { z } from "zod" ;
// All Entity Enums
export const UserRole = z . enum ([ "admin" , "normal" ]);
export type UserRole = z . infer < typeof UserRole >;
export const UserSearchField = z . enum ([ "id" , "email" , "username" ]);
export const UserOrderBy = z . enum ([ "id-desc" , "id-asc" , "created_at-desc" ]);
// Subset types
export type UserSubsetKey = "A" | "P" | "SS" ;
export type UserSubsetMapping = {
A : UserA ;
P : UserP ;
SS : UserSS ;
};
// Enum label helpers
export function userRoleLabel ( role : UserRole ) : string {
return {
admin: "Administrator" ,
normal: "Normal User" ,
}[ role ];
}
// Export all models
export * from "./user/user.model" ;
export * from "./post/post.model" ;
Generated when : Entity save or pnpm sonamu sync
Editable : ❌ Auto-regenerated
3. Subset Queries (sonamu.generated.sso.ts)
Query functions per Subset.
api/src/application/sonamu.generated.sso.ts (auto-generated)
import type { PuriWrapper } from "sonamu" ;
// Subset query functions
export const userSubsetQueries = {
A : ( puri : PuriWrapper ) =>
puri
. table ( "users" )
. select ([
"users.id" ,
"users.email" ,
"users.username" ,
"users.role" ,
"users.created_at" ,
]),
P : ( puri : PuriWrapper ) =>
puri
. table ( "users" )
. select ([
"users.id" ,
"users.email" ,
"users.username" ,
])
. leftJoin ( "employees" , "employees.user_id" , "users.id" )
. select ([
"employees.id as employee__id" ,
"employees.department_id as employee__department_id" ,
]),
SS : ( puri : PuriWrapper ) =>
puri
. table ( "users" )
. select ([ "users.id" , "users.email" ]),
};
// Loader query functions (HasMany, ManyToMany)
export const userLoaderQueries = {
P: [
{
as: "posts" ,
refId: "id" ,
qb : ( puri : PuriWrapper , ids : number []) =>
puri
. table ( "posts" )
. whereIn ( "posts.user_id" , ids )
. select ([
"posts.id" ,
"posts.user_id as refId" ,
"posts.title" ,
]),
},
],
};
Generated when : Entity Subset changes
Editable : ❌ Auto-regenerated
4. API Services (services.generated.ts)
API client functions.
web/src/services/services.generated.ts (auto-generated)
import axios from "axios" ;
import type { ListResult } from "./sonamu.shared" ;
import type { User , UserListParams , UserSaveParams } from "./user/user.types" ;
// Axios clients
export async function findUserById ( id : number ) : Promise < User > {
const { data } = await axios . get ( "/user/findById" , { params: { id } });
return data ;
}
export async function findManyUsers (
params ?: UserListParams
) : Promise < ListResult < UserListParams , User >> {
const { data } = await axios . get ( "/user/findMany" , { params });
return data ;
}
export async function saveUser ( params : UserSaveParams []) : Promise < number []> {
const { data } = await axios . post ( "/user/save" , { params });
return data ;
}
// TanStack Query Hooks
export function useUserById ( id : number ) {
return useQuery ({
queryKey: [ "User" , "findById" , id ],
queryFn : () => findUserById ( id ),
});
}
export function useUsers ( params ?: UserListParams ) {
return useQuery ({
queryKey: [ "Users" , "findMany" , params ],
queryFn : () => findManyUsers ( params ),
});
}
// TanStack Mutation Hooks
export function useSaveUser () {
return useMutation ({
mutationFn : ( params : UserSaveParams []) => saveUser ( params ),
});
}
Generated when : Model’s @api decorator changes
Editable : ❌ Auto-regenerated
Target-specific generation : Copied to each target specified in sync.targets in sonamu.config.ts (web, app, etc.).
5. HTTP Test File (sonamu.generated.http)
HTTP test file for REST Client.
api/sonamu.generated.http (auto-generated)
@ baseUrl = http://localhost:3000
### User.findById
GET {{baseUrl}}/user/findById?id=1
### User.findMany
GET {{baseUrl}}/user/findMany?num=10&page=1
### User.save
POST {{baseUrl}}/user/save
Content-Type : application/json
{
"params" : [
{
"email" : "test@test.com" ,
"username" : "Test User"
}
]
}
### User.del
DELETE {{baseUrl}}/user/del
Content-Type : application/json
{
"ids" : [ 1 , 2 , 3 ]
}
Generated when : Model file changes
Editable : ❌ Auto-regenerated
Usage : Install VS Code’s REST Client extension and execute requests
React Components
React UI components can be generated via Scaffold.
6. List Component
web/src/pages/user/UserList.tsx
web/src/pages/user/UserSearchInput.tsx
import { useUsers } from "@/services/services.generated" ;
export function UserList () {
const [ params , setParams ] = useState ({ num: 20 , page: 1 });
const { data , isLoading } = useUsers ( params );
return (
< div >
< table >
< thead >
< tr >
< th > ID </ th >
< th > Email </ th >
< th > Username </ th >
</ tr >
</ thead >
< tbody >
{ data ?. rows . map (( user ) => (
< tr key = { user . id } >
< td > { user . id } </ td >
< td > { user . email } </ td >
< td > { user . username } </ td >
</ tr >
)) }
</ tbody >
</ table >
< Pagination
total = { data ?. total ?? 0 }
page = { params . page }
onChange = { ( page ) => setParams ({ ... params , page }) }
/>
</ div >
);
}
Generated when : pnpm sonamu generate view_list --entity User
Editable : ✅ Editable after initial generation
web/src/pages/user/UserForm.tsx
import { useSaveUser } from "@/services/services.generated" ;
import { UserSaveParams } from "@/services/user/user.types" ;
export function UserForm ({ userId } : { userId ?: number }) {
const { data : user } = useUserById ( userId );
const { mutate : save } = useSaveUser ();
const handleSubmit = ( values : UserSaveParams ) => {
save ([ values ], {
onSuccess : () => {
alert ( "Saved" );
},
});
};
return (
< form onSubmit = { handleSubmit } >
< input
name = "email"
type = "email"
defaultValue = { user ?. email }
required
/>
< input
name = "username"
defaultValue = { user ?. username }
required
/>
< select name = "role" defaultValue = { user ?. role ?? "normal" } >
< option value = "admin" > Administrator </ option >
< option value = "normal" > Normal User </ option >
</ select >
< button type = "submit" > Save </ button >
</ form >
);
}
Generated when : pnpm sonamu generate view_form --entity User
Editable : ✅ Editable after initial generation
8. Select Components
web/src/components/user/UserIdAsyncSelect.tsx
web/src/components/user/UserRoleSelect.tsx
// Async Select (searchable)
export function UserIdAsyncSelect ({
value ,
onChange ,
} : {
value ?: number ;
onChange : ( value : number ) => void ;
}) {
const [ keyword , setKeyword ] = useState ( "" );
const { data } = useUsers ({ keyword , num: 10 });
return (
< AsyncSelect
value = { value }
onChange = { onChange }
onSearch = { setKeyword }
options = { data ?. rows . map (( u ) => ({
value: u . id ,
label: u . email ,
})) }
/>
);
}
Files for Server-Side Rendering are auto-generated.
9. Queries (queries.generated.ts)
web/src/queries.generated.ts (auto-generated)
import { queryOptions } from "@tanstack/react-query" ;
import { findUserById , findManyUsers } from "./services/services.generated" ;
export const userQueries = {
findById : ( id : number ) =>
queryOptions ({
queryKey: [ "User" , "findById" , id ],
queryFn : () => findUserById ( id ),
}),
findMany : ( params ?: UserListParams ) =>
queryOptions ({
queryKey: [ "Users" , "findMany" , params ],
queryFn : () => findManyUsers ( params ),
}),
};
Generated when : Model file changes
Editable : ❌ Auto-regenerated
10. Entry Server (entry-server.generated.tsx)
web/src/entry-server.generated.tsx (auto-generated)
import { dehydrate , QueryClient } from "@tanstack/react-query" ;
import { userQueries } from "./queries.generated" ;
export async function loader ({ params }) {
const queryClient = new QueryClient ();
// SSR data prefetching
if ( params . userId ) {
await queryClient . prefetchQuery (
userQueries . findById ( Number ( params . userId ))
);
}
return {
dehydratedState: dehydrate ( queryClient ),
};
}
Generated when : Model file changes
Editable : ❌ Auto-regenerated
Internationalization Files
Generated when i18n configuration exists.
11. Sonamu Dictionary (sd.generated.ts)
api/src/sd.generated.ts (auto-generated)
export const SD = {
User: "User" ,
user: {
id: "ID" ,
email: "Email" ,
username: "Username" ,
role: "Role" ,
created_at: "Created At" ,
},
UserRole: {
admin: "Administrator" ,
normal: "Normal User" ,
},
} as const ;
Generated when : Entity or i18n file changes
Editable : ❌ Auto-regenerated
Targets : Generated for api, web, app each
Generated Files Summary Table
File Location Generated When Editable {entity}.types.tsapi/src/application/{entity}/Entity save ❌ sonamu.generated.tsapi/src/application/Entity save ❌ sonamu.generated.sso.tsapi/src/application/Entity save ❌ services.generated.tsweb/src/services/Model change ❌ sonamu.generated.httpapi/Model change ❌ queries.generated.tsweb/src/Model change ❌ entry-server.generated.tsxweb/src/Model change ❌ sd.generated.tsapi/src/, web/src/, app/src/Entity/i18n change ❌ {Entity}List.tsxweb/src/pages/{entity}/Scaffold ✅ {Entity}Form.tsxweb/src/pages/{entity}/Scaffold ✅ {Entity}SearchInput.tsxweb/src/pages/{entity}/Scaffold ✅ {Entity}IdAsyncSelect.tsxweb/src/components/{entity}/Scaffold ✅ {EnumId}Select.tsxweb/src/components/{entity}/Scaffold ✅
Auto-regenerated files : Never modify *.generated.* files. Changes will be automatically overwritten.
Next Steps