Sonamu automatically generates type definitions and Service code when Entities and APIs change, synchronizing them to frontend projects. Use sync.targets to specify the frontends to sync to.
Basic Structure
import { defineConfig } from "sonamu";
export default defineConfig({
sync: {
targets: ["web"], // Sync to web folder
},
// ...
});
targets
Specifies the list of frontend projects to synchronize.
Type: string[] (required)
export default defineConfig({
sync: {
targets: ["web", "app"], // Sync to 2 projects
},
});
How It Works
When Entities or APIs change, Sonamu automatically generates and synchronizes the following files to each target directory:
-
Type Definitions (
{entity}.types.ts)
- TypeScript types generated from Entity fields
- Zod validation schemas
- Subset types
-
Service Classes (
{entity}.service.ts)
- Type-safe methods for API calls
- TanStack Query integration ready
Directory Structure
Single Frontend
The most common case, using only one web frontend.
export default defineConfig({
sync: {
targets: ["web"],
},
// ...
});
Project structure:
Sync location: web/src/services/
Multiple Frontends
When developing web and app (React Native, Flutter, etc.) simultaneously:
export default defineConfig({
sync: {
targets: ["web", "app"],
},
// ...
});
Project structure:
Sync locations:
web/src/services/
app/src/services/
Each frontend shares the same types and Services, so all clients are automatically updated when APIs change.
target Directory Location
By default, Sonamu looks for directories with the same name as the target in the project root.
Custom Paths
If target directories are in different locations, you can use relative paths:
export default defineConfig({
sync: {
targets: ["../frontend/web", "../mobile/app"],
},
});
Project structure:
Verifying Sync
When you modify and save an Entity, sync messages appear in the console:
β¨ Syncing to targets...
β web/src/services/user.types.ts
β web/src/services/user.service.ts
β app/src/services/user.types.ts
β app/src/services/user.service.ts
β
All files are synced!
Generated Files
Two files are generated for each Entity:
user.types.ts:
// Auto-generated types
export type UserSaved = {
id: number;
email: string;
name: string;
createdAt: Date;
};
export type UserForm = {
email: string;
name: string;
};
// Zod schema
export const UserFormSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
user.service.ts:
// Auto-generated Service
export class UserService {
static async list(params: UserListParams): Promise<UserSaved[]> {
return apiClient.get("/api/user/list", { params });
}
static async detail(id: number): Promise<UserSaved> {
return apiClient.get(`/api/user/${id}/detail`);
}
static async save(form: UserForm): Promise<UserSaved> {
return apiClient.post("/api/user/save", form);
}
}
Disabling Sync
If thereβs no frontend or sync is not needed, specify an empty array:
export default defineConfig({
sync: {
targets: [], // No sync
},
// ...
});
Sync can be disabled for API-only projects (e.g., microservices).
Practical Examples
Basic Web Project
import { defineConfig } from "sonamu";
export default defineConfig({
projectName: "MyApp",
sync: {
targets: ["web"],
},
// ...
});
Project structure:
Web + Mobile App
import { defineConfig } from "sonamu";
export default defineConfig({
projectName: "EcommerceApp",
sync: {
targets: ["web", "mobile"],
},
// ...
});
Project structure:
Monorepo Structure
import { defineConfig } from "sonamu";
export default defineConfig({
projectName: "MonorepoApp",
sync: {
targets: [
"../packages/web-client",
"../packages/admin-panel",
"../packages/mobile-app",
],
},
// ...
});
Project structure:
Sync Cautions
1. Donβt Modify Auto-generated Files
Synced files are auto-generated, so donβt modify them directly.
// β Don't modify!
// web/src/services/user.service.ts
export class UserService {
static async list() {
// Code added here will be lost on next sync!
}
}
Use a wrapper class instead:
// β
Manage in separate file
// web/src/services/user-wrapper.ts
import { UserService } from "./user.service";
export class UserServiceWrapper {
static async listActiveUsers() {
const users = await UserService.list();
return users.filter(u => u.status === "active");
}
}
2. Git Management
Commit synced files to Git.
# β Don't add to .gitignore
# web/src/services/
# β
Should be included in Git
Reasons:
- Team members can develop frontend without running backend
- CI/CD can build frontend without Sonamu
3. Verify target Directory Exists
The directory specified as target must exist.
# Error if directory doesn't exist
β Error: Sync target 'web' not found at ../web
Solution:
# Create target directories
mkdir web
mkdir app
Frontend Setup
Some configuration is needed in the frontend to use synced Services.
1. API Client Setup
web/src/lib/api-client.ts:
import axios from "axios";
export const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL ?? "http://localhost:1028",
withCredentials: true,
});
2. Using Services
import { UserService } from "@/services/user.service";
// API calls
const users = await UserService.list();
const user = await UserService.detail(1);
β Frontend Integration Guide
Next Steps
After completing sync target configuration: