Learn how Sonamu detects API changes at compile time to prevent runtime errors in advance.
Compile-time Errors Overview
Instant Detection On API changes Compile-time errors
Runtime Safety Discover before deploy Production stability
IDE Integration Red underlines Immediate feedback
Safe Refactoring Large-scale changes Impact range visibility
What are Compile-time Errors?
Problem: Errors Only Discovered at Runtime
In traditional development, API changes are only discovered at runtime .
// β Field name changed in backend
// username β displayName
// Frontend (unaware of change)
function UserProfile ({ userId } : { userId : number }) {
const user = await fetchUser ( userId );
return (
< div >
{ /* Runtime error! username field doesn't exist */ }
< h1 >{user. username } </ h1 >
</ div >
);
}
Problems with runtime errors :
Occurs in production : Users experience directly
Delayed discovery : Only discovered via QA or user reports
Hard to debug : Need to trace where problem occurred
Trust degradation : Service stability decreases
Solution: Compile-time Errors
Sonamu detects API changes immediately at compile time .
// β
Field name changed in backend
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : {
id : number ;
displayName : string ; // username β displayName
email : string ;
};
}> {
// Implementation...
}
// Run pnpm generate β Service regenerated
// Frontend (compile-time error occurs!)
function UserProfile ({ userId } : { userId : number }) {
const { data } = UserService . useUser ( "A" , userId );
return (
< div >
{ /* β Compile error! Property 'username' does not exist */ }
< h1 >{data.user. username } </ h1 >
{ /* β
After fix */ }
< h1 >{data.user. displayName } </ h1 >
</ div >
);
}
Benefits of compile-time errors :
β¨ Instant discovery : IDE shows immediately while coding
β¨ Fix before deploy : Resolve all issues before production deploy
β¨ Impact range visibility : See all places affected by change
β¨ Auto refactoring : Use IDEβs Rename feature
Error Detection Scenarios
1. Field Name Change
// Backend: username β displayName
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : { id : number ; displayName : string ; }
}> {
// Implementation...
}
// After pnpm generate
// Frontend: error at all user.username usage points
const { data } = await UserService . getUser ( "A" , 123 );
console . log ( data . user . username ); // β Compile error
IDE error message :
Property 'username' does not exist on type '{ id: number; displayName: string; }'.
Did you mean 'displayName'?
2. Field Type Change
// Backend: changed age from number to string
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : { id : number ; age : string ; } // number β string
}> {
// Implementation...
}
// Frontend
const { data } = await UserService . getUser ( "A" , 123 );
const nextYear = data . user . age + 1 ;
// β Compile error: string + number
IDE error message :
Operator '+' cannot be applied to types 'string' and 'number'.
3. Required/Optional Change
// Backend: changed bio from required to optional
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : {
id : number ;
bio ?: string ; // string β string | undefined
};
}> {
// Implementation...
}
// Frontend
const { data } = await UserService . getUser ( "C" , 123 );
const bioLength = data . user . bio . length ;
// β Compile error: bio could be undefined
IDE error message :
Object is possibly 'undefined'.
Fix :
// β
Use optional chaining
const bioLength = data . user . bio ?. length ;
// β
Or null check
if ( data . user . bio ) {
const bioLength = data . user . bio . length ;
}
4. Field Deletion
// Backend: profile field removed
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : {
id : number ;
username : string ;
// profile field deleted
};
}> {
// Implementation...
}
// Frontend
const { data } = await UserService . getUser ( "C" , 123 );
console . log ( data . user . profile . avatar );
// β Compile error: Property 'profile' does not exist
5. Parameter Change
// Backend: parameter type changed
@ api ({ httpMethod: "GET" })
async searchUsers ( query : {
keyword: string ;
limit : number ; // string β number
}): Promise < { users : User [] } > {
// Implementation...
}
// Frontend
await UserService . searchUsers ({
keyword: "john" ,
limit: "10" , // β Compile error: string not assignable to number
});
IDE error message :
Type 'string' is not assignable to type 'number'.
6. Return Type Change
// Backend: changed from single object to array
@ api ({ httpMethod: "GET" })
async getPosts (): Promise <{
posts : Post []; // Post β Post[]
}> {
// Implementation...
}
// Frontend
const { data } = await PostService . getPosts ();
console . log ( data . posts . title );
// β Compile error: Property 'title' does not exist on type 'Post[]'
Fix :
// β
Handle array
data . posts . forEach (( post ) => {
console . log ( post . title );
});
Refactoring Support
Large-scale Changes are Safe
You can immediately identify all locations affected by API changes.
// Scenario: Major restructuring of User entity
// Backend (before)
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : {
id : number ;
name : string ;
email : string ;
};
}> {
// ...
}
// Backend (after)
@ api ({ httpMethod: "GET" })
async getUser (): Promise <{
user : {
id : number ;
profile : {
displayName : string ;
contactEmail : string ;
};
};
}> {
// ...
}
After running pnpm generate :
All error locations are displayed in IDE:
src/components/UserProfile.tsx:15 - Property 'name' does not exist
src/components/UserCard.tsx:8 - Property 'name' does not exist
src/pages/users/[id].tsx:42 - Property 'email' does not exist
... (23 locations total)
Fix process :
Check all errors in IDEβs βProblemsβ panel
Visit each location and fix to match new structure
When all errors are resolved, compilation succeeds
Deploy safely
Leverage IDEβs Auto Refactoring
You can utilize TypeScriptβs powerful refactoring features.
Example: Batch Field Name Change
Check type definition in Service file
Position cursor on field name
F2 (Rename Symbol) or right-click β Rename
Enter new name
All usages are automatically changed
// β
Batch change username β displayName
// IDE automatically changes throughout project
// Before
data . user . username ;
// After (auto-changed)
data . user . displayName ;
Practical Workflow
1. Backend API Change
// backend/models/user.model.ts
@ api ({ httpMethod: "GET" })
async getProfile (): Promise <{
user : {
id : number ;
displayName : string ; // Changed: username β displayName
email : string ;
};
}> {
// Implementation...
}
2. Service Regeneration
Output :
β Analyzing API methods...
β Generating services.generated.ts...
β Generating types...
β Done!
3. TypeScript Compile
Output (error occurs) :
src/components/UserProfile.tsx:15:23 - error TS2339:
Property 'username' does not exist on type '{ id: number; displayName: string; email: string; }'.
15 <h1>{data.user.username}</h1>
~~~~~~~~
src/components/UserCard.tsx:8:35 - error TS2339:
Property 'username' does not exist on type '{ id: number; displayName: string; email: string; }'.
8 const name = user.username;
~~~~~~~~
Found 2 errors.
4. Fix Errors
// src/components/UserProfile.tsx
- < h1 > {data.user. username } </ h1 >
+ < h1 > {data.user. displayName } </ h1 >
// src/components/UserCard.tsx
- const name = user . username ;
+ const name = user . displayName ;
5. Re-verify
Output (success) :
6. Deploy Safely
All type errors are resolved, so you can deploy safely.
CI/CD Integration
Add TypeScript Check to CI
# .github/workflows/ci.yml
name : CI
on : [ push , pull_request ]
jobs :
typecheck :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v2
- name : Setup Node.js
uses : actions/setup-node@v2
with :
node-version : "22"
- name : Install dependencies
run : pnpm install
- name : Generate Services
run : pnpm generate
- name : TypeScript Check
run : pnpm tsc --noEmit
Benefits :
Auto-detect type errors in Pull Requests
Block merge if type errors exist
Ensure code quality across the team
Best Practices
1. Regular Service Regeneration
# When starting development
pnpm generate
# After backend changes
pnpm generate
# After pull
pnpm generate
2. Pre-commit Hook Setup
// package.json
{
"husky" : {
"hooks" : {
"pre-commit" : "pnpm tsc --noEmit"
}
}
}
Blocks commit if type errors exist.
3. VSCode Settings
// .vscode/settings.json
{
"typescript.tsdk" : "node_modules/typescript/lib" ,
"typescript.enablePromptUseWorkspaceTsdk" : true ,
"editor.codeActionsOnSave" : {
"source.fixAll" : true
}
}
4. Donβt Ignore Type Errors
// β Ignoring type error (dangerous!)
// @ts-ignore
console . log ( data . user . username );
// β
Correct fix
console . log ( data . user . displayName );
Never use @ts-ignore. Type errors indicate real problems.
Understanding Error Messages
Common Error Messages
1. Property does not exist
Property 'username' does not exist on type 'User'.
β Field was deleted or renamed
2. Type is not assignable
Type 'string' is not assignable to type 'number'.
β Type was changed
3. Object is possibly undefined
Object is possibly 'undefined'.
β Changed from required to optional
4. Cannot find name
Cannot find name 'UserService'.
β Service not regenerated or import missing
5. Expected N arguments, but got M
Expected 2 arguments, but got 1.
β API parameters added/removed
Cautions
Cautions when handling compile-time errors : 1. pnpm generate required : Always run after
API changes 2. No @ts-ignore : Donβt ignore type errors 3. No any type : Breaks type safety
4. Fix all errors : Even one left can cause runtime error 5. CI/CD integration : Set up auto
type checking
Next Steps
API Type Inference Understanding type inference
Shared Types Type file structure
Using Services Service usage