registerInstance and registerClassConstructor
@Cat registers methods in the QA catalog (UserService.findById, etc.). For class-style entries (style: 'class'), RPC must also know which object to call those methods on (this).
That is what this page covers: instance registration (your live object) and optional constructor registration (fallback new ClassName()).
Not needed for cat() or catModule() — those invoke the function directly with no class instance.
When do you need registerInstance?
| Situation | Need registerInstance? |
|---|---|
Class with @Cat methods and empty / safe zero-arg constructor | No — first RPC can new YourClass() automatically |
Constructor needs dependencies (db, config, logger, …) | Yes — build the service in your app boot and pass it in |
| You want QA to use the same singleton as production HTTP handlers | Yes — register that shared instance |
Plain functions via cat / catModule | No |
How RPC picks an instance
For fnKey OrdersService.placeOrder, the SDK looks up OrdersService in this order:
registerInstance(yourObject)— ifyourObject.constructor.name === 'OrdersService', use it.- Cached singleton — instance created earlier by auto-
newin this process. new OrdersService()— only if a constructor was registered (happens automatically when you use@Caton a method).NO_INSTANCE— none of the above worked.
The class name in the fnKey must match instance.constructor.name (the JavaScript class name), not a filename or variable name.
Example A — no registerInstance (demo / simple services)
Constructor has no required parameters. @Cat on methods is enough.
import 'reflect-metadata'
import { Cat, Return } from '@gloocan/cat-inspector'
class PricingEngine {
@Cat
computeDiscount(yearsActive: number, monthlySpend: number) {
return Return('DISCOUNT', { rate: 0.1 })
}
}
// Import this file at server startup so decorators run.
// First RPC to PricingEngine.computeDiscount → SDK runs new PricingEngine() once.Same idea as the cat-demo showcase class: no boot-time registerInstance call.
Example B — with dependencies (typical production)
import 'reflect-metadata'
import { Cat, Return, Throw, registerInstance } from '@gloocan/cat-inspector'
interface OrderRepo {
findById(id: string): Promise<{ id: string; total: number } | null>
}
class OrdersService {
constructor(private readonly repo: OrderRepo) {}
@Cat
async placeOrder(orderId: string, userId: string) {
const order = await this.repo.findById(orderId)
if (!order) Throw('NOT_FOUND', new Error('order missing'))
return Return('PLACED', { orderId, userId, total: order.total })
}
}
// After you create your real dependencies:
const repo: OrderRepo = createOrderRepo(prisma)
registerInstance(new OrdersService(repo))
// RPC OrdersService.placeOrder → uses THIS instance (with prisma-backed repo).
// SDK will NOT call new OrdersService() with zero args.Call registerInstance once at application startup (after modules with @Cat are loaded, before accepting QA traffic).
Example C — shared instance for HTTP and QA
Your Express app and QA should share one service object:
const orders = new OrdersService(orderRepo)
app.use('/api', ordersRouter) // HTTP uses `orders` via closure or DI container
registerInstance(orders) // QA RPC uses the same `orders`registerInstance(instance: object): void
function registerInstance(instance: object): voidBehavior:
- Keys the instance by
instance.constructor.name(must be non-empty). - Duplicate registration for the same class name throws at register time.
- Passing a non-object is ignored (no-op).
Common mistakes:
| Mistake | Result |
|---|---|
Class renamed but fnKey still uses old name | NO_INSTANCE or wrong class |
Anonymous class class { … } | Constructor name may be empty → register fails silently |
Forgot to import file with @Cat before registerInstance | Catalog empty or instance not wired to entries |
Registered instance after first RPC relied on auto-new | First callers may have used a different singleton; register early |
registerClassConstructor (low-level)
function registerClassConstructor(
className: string,
ctor: new (...args: any[]) => any,
): voidRegisters “how to new this class” for step 3 in the resolution order. @Cat on any method already does this for you using the real class constructor.
You rarely call registerClassConstructor directly. Prefer:
@Caton methods — catalog + constructor hook, orregisterInstance— your constructed object with deps.
Direct use only when the registered name must differ from normal @Cat behavior (advanced).
Error: NO_INSTANCE
Returned by executeRPC when:
- No
registerInstancefor that class name, - No cached auto-instance,
- No constructor registered (no
@Catran on that class yet),
and the method cannot be invoked.
Fix: Ensure the module defining the class is imported, @Cat is on the method, and either register an instance or use a zero-arg-safe constructor.
Quick comparison
| Registration style | Catalog (fnKey) | Instance for RPC |
|---|---|---|
cat('Billing.refund', fn) | Yes | Not used (calls fn) |
catModule('Billing', { refund() {} }) | Yes | Not used |
@Cat on class methods | Yes | registerInstance or auto new ClassName() |
See also
@Catdecorator — method registration and auto-constructor hook- Registry and
fnKey—ClassName.methodNamekeys - Cat modes —
servicevs Expressapi/api_candidate - RPC pipeline —
executeRPCandNO_INSTANCE - RPC error codes — machine-readable codes for UIs