Skip to main content
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

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

7. Form Component

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

// 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)

For APIs with the tanstack-query client enabled, this file exports SSR-side descriptors (SSRQuery) grouped by model namespace. The api-side ssr/routes.ts uses these descriptors via registerSSR(...) to compose preload configs.
api/src/application/queries.generated.ts (auto-generated)
import type { SSRQuery } from "sonamu/ssr";

function createSSRQuery(
  modelName: string,
  methodName: string,
  params: any[],
  serviceKey: [string, string],
): SSRQuery {
  return { modelName, methodName, params, serviceKey, __brand: "SSRQuery" } as SSRQuery;
}

export namespace UserService {
  export const findById = (id: number): SSRQuery =>
    createSSRQuery("UserModel", "findById", [id], ["User", "findById"]);
}
Generated when: Model file changes Editable: ❌ Auto-regenerated Location: An api-only asset. It is not copied into target (web/app) and is never imported directly from target code. The SSR renderer executes the descriptor server-side and only the result is hydrated on the client.

10. Entry Server (entry-server.generated.tsx)

TanStack Router-based SSR entry point. It exports a render(url, preloadedData) function called by the web side, not a per-route loader. Data prefetched during SSR (preloadedData) is injected via queryClient.setQueryData(queryKey, data), then dehydrated for client hydration.
web/src/entry-server.generated.tsx (auto-generated)
import { QueryClient, dehydrate } from "@tanstack/react-query";
import { createMemoryHistory, createRouter, RouterProvider } from "@tanstack/react-router";
import { Suspense } from "react";
import { renderToString } from "react-dom/server";
import { routeTree } from "./routeTree.gen";

export type PreloadedData = {
  queryKey: any[];
  data: any;
};

export async function render(url: string, preloadedData: PreloadedData[] = []) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { staleTime: 5000, retry: false },
    },
  });

  // Inject results pre-invoked during SSR into queryClient
  for (const { queryKey, data } of preloadedData) {
    queryClient.setQueryData(queryKey, data);
  }

  const dehydratedState = dehydrate(queryClient);

  const memoryHistory = createMemoryHistory({ initialEntries: [url] });
  const router = createRouter({
    routeTree,
    context: { queryClient },
    history: memoryHistory,
    defaultPreload: "intent",
  });
  await router.load();

  const appHtml = renderToString(
    <Suspense fallback={null}>
      <RouterProvider router={router} />
    </Suspense>,
  );

  return { html: appHtml, dehydratedState };
}
Generated when: Once on first sync (static bootstrap asset, does not depend on inputs) 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

FileLocationGenerated WhenEditable
{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.tsapi/src/application/Model change
entry-server.generated.tsxweb/src/Once on first sync
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

Syncer

Understanding the core Syncer system

When Files Regenerate

Learn when files are regenerated

Customizing Generated Code

How to customize generated code

Creating Entities

Define Entities and generate files