Skip to main content
Boundaries define the limits where the HMR system can safely replace modules. Understanding Boundaries properly when developing with Sonamu allows you to maximize HMR benefits.

What is a Boundary?

A Boundary means a β€œreloadable limit”. The HMR system operates by the following rules:
  • Boundary files: Files that can be reloaded on change (Model, API, Entity, etc.)
  • Non-boundary files: Files that require a full server restart on change (config, environment variables, etc.)
βœ… Boundary files: Changes are instantly reflected via HMR ❌ Non-boundary files: Require server restart on change

Why are Boundaries Necessary?

Static imports execute only once when Node.js starts, so they cannot be replaced at runtime:
Files designated as Boundaries must be imported dynamically, so that the latest code can be fetched on change.

Sonamu’s Boundary Configuration

By default, Sonamu sets all TypeScript files in the project as Boundaries:
With this configuration, all files under src/ are HMR-capable and automatically reload on change.

Boundary File Examples

Boundary files (HMR capable):
  • user.model.ts - Model file
  • user.api.ts - API file
  • user.entity.ts - Entity definition
  • helpers.ts - Utility functions
  • constants.ts - Constants definition
Non-boundary files (restart required):
  • sonamu.config.ts - Sonamu configuration
  • .env - Environment variables
  • package.json - Dependencies

Boundary Rules

1. Dynamic Import Required

Boundary files must be imported dynamically. ❌ Wrong: Static import
βœ… Correct: Dynamic import

Sonamu Handles This Automatically

Fortunately, Sonamu’s Syncer automatically handles dynamic imports for Entity-based files:
In other words, you don’t need to worry about this in typical Sonamu development!

2. Static Imports Between Boundaries Allowed

Static imports between Boundary files are allowed (Sonamu’s enhancement):
This works because:
  • Both files are Boundaries
  • Both are dynamically loaded by Syncer
  • HMR works correctly even when they reference each other

3. Non-boundary to Boundary Import Restriction

HMR won’t work if a non-boundary file statically imports a Boundary:

Practical Scenarios

Using HMR in User-Post Relationship

Situation: Adding a role field to User Entity and adding author permission check in Post API. Step 1: Modify Entity
On save, HMR automatically:
  • Updates user.entity.ts
  • Regenerates user.types.ts
  • Reloads UserModel
Console output:
Step 2: Add Model Logic
On save, HMR:
  • Reloads UserModel
  • Reloads all APIs that use UserModel
Step 3: Add Permission Check to API
On save, HMR:
  • Re-registers PostApi.create
  • Testable immediately without server restart!

Traditional approach:
  1. Modify Entity
  2. Manually generate Types file
  3. Modify Model file
  4. Server restart (30 seconds)
  5. Modify API file
  6. Server restart (30 seconds)
  7. Test
Sonamu + HMR:
  1. Modify Entity (auto-generated)
  2. Modify Model file (auto-reload)
  3. Modify API file (auto-reload)
  4. Test βœ…
3 restarts (90 seconds) reduced to 0!

Complex Business Logic Development

Situation: Implementing inventory check, payment processing, and notification on order creation. File structure:
Step 1: Inventory Check Logic
Save β†’ 2 seconds later β†’ Instantly reflected βœ… Step 2: Order Creation Logic
Save β†’ ProductModel dependency also auto-reloaded βœ… Step 3: API Endpoint
Save β†’ API re-registered β†’ Test immediately in Postman! HMR log:

import.meta.hot API

In Boundary files, you can use the import.meta.hot API to finely control HMR behavior.

dispose() - Resource Cleanup

Perform cleanup before module reload:
Cases requiring resource cleanup:
  • Clear timers/intervals
  • Remove event listeners
  • Close WebSocket connections
  • Clean up database connection pools
  • Close file handles
Practical usage example:

decline() - Require Full Restart

Exclude specific modules from HMR and require full restart:
When to use decline():
  • Global configuration files
  • Database connection settings
  • Singletons with complex initialization
  • Modules with difficult state management
Practical usage example:

boundary Object - State Sharing

Share data between Boundaries to maintain state after reload:
Practical usage example:
The boundary object maintains data between HMR cycles, but is reset on server restart. Use database or Redis for data requiring persistent storage.

Type Definitions

To use import.meta.hot in TypeScript, type definitions are needed:
Sonamu projects already include type definitions, so no separate configuration is needed.

Debugging

Check Boundary Configuration

Verify if a specific file is configured as a Boundary:
Output example:

Visualize Dependency Tree

Output:
If HMR isn’t working during development: 1. Verify the file is designated as a Boundary 2. Check if it’s being imported dynamically 3. Check for circular dependencies

Performance Optimization

Exclude Unnecessary Boundaries

Setting all files as Boundaries can slow things down due to a large dependency tree:

Minimize Dependencies

Avoid circular references between Models and only import what’s needed:

Summary

Sonamu’s Boundary system: βœ… Automatic handling: Syncer automatically handles dynamic imports for Entity-based files βœ… Cross-boundary references allowed: Static imports between Models permitted βœ… Fine-grained control: Resource cleanup and state maintenance via import.meta.hot API βœ… Debugging tools: Check dependency tree with hot.dump() In most cases, you don’t need to worry about Boundaries - Sonamu handles it automatically!