Skip to main content
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:
  1. Type Definitions ({entity}.types.ts)
    • TypeScript types generated from Entity fields
    • Zod validation schemas
    • Subset types
  2. 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: