Skip to content

Changelog - 2026-03-15 ​

gRPC Controller System & Controller Architecture Refactor ​

Introduces a full gRPC controller system built on ConnectRPC, alongside a structural refactor that splits the controller hierarchy into dedicated REST and gRPC base classes. The current version supports unary RPCs only (streaming decorators exist but throw at boot time - HTTP/1.1 Connect protocol limitation). The system uses AsyncLocalStorage for production-safe request context isolation, enforces authentication/authorization on gRPC endpoints, and integrates into the existing application lifecycle through a component-based registration pattern.

Overview ​

  • gRPC Controller System: New BaseGrpcController with ConnectRPC integration (unary RPCs only in current version)
  • Controller Architecture Split: REST and gRPC controllers now have dedicated base classes under base/controllers/rest/ and base/controllers/grpc/
  • AsyncLocalStorage Isolation: GrpcRequestAdapter uses AsyncLocalStorage for production-safe per-request context, replacing the previous createHonoConnectAdapter function
  • gRPC Auth Enforcement: Authentication and authorization middleware applied to gRPC endpoints via pre-built middleware chain (symmetric with REST)
  • gRPC Error Code Preservation: ConnectRPC error codes preserved instead of always mapping to INTERNAL(13)
  • Unified IRpcRegistration Type: Single type merging handler, metadata, and pre-built auth middleware
  • RPC Decorators: @unary for declarative RPC method registration. @serverStream, @clientStream, @bidiStream decorators exist but throw at boot time (reserved for future HTTP/2 support)
  • Optional ConnectRPC Dependency: @connectrpc/connect added as an optional peer dependency -- REST-only users are unaffected
  • GRPC Protocol Constants: Headers, methods, status codes, and content types added to the helpers package
  • Component-Based Registration: GrpcComponent and RestComponent for transport-specific controller registration

Breaking Changes ​

WARNING

This section contains changes that require migration or manual updates to existing code.

1. Controller Base Classes Renamed and Relocated ​

The controller base classes have been split into transport-specific directories. The old files (base/controllers/abstract.ts and base/controllers/base.ts) are removed.

Before:

typescript
import { AbstractController, BaseController } from '@venizia/ignis';
// or
import { AbstractController } from '../base/controllers/abstract';
import { BaseController } from '../base/controllers/base';

After:

typescript
import { AbstractRestController, BaseRestController } from '@venizia/ignis';
// or
import { AbstractRestController } from '../base/controllers/rest/abstract';
import { BaseRestController } from '../base/controllers/rest/base';

REST-only users: BaseController continues to work via re-exports from the barrel index. No migration required unless you import directly from the deleted file paths.

2. gRPC Controller Internals Refactored ​

If you were building custom gRPC controllers against the previous internal API, the following changes apply:

Before:

typescript
class MyGrpcController extends AbstractGrpcController {
  implementation = { /* handler map */ };
  definitions: Record<string, IRpcMetadata> = { /* separate metadata map */ };

  getImplementation() { return this.implementation; }
}

// Adapter creation
const adapter = createHonoConnectAdapter(options);

After:

typescript
class MyGrpcController extends AbstractGrpcController {
  // Unified map - single write per RPC:
  // this.definitions[configs.name] = { configs, handler };

  // Structure: IRpcRegistration<RouteEnv>
  // {
  //   configs: IRpcMetadata;    // { name, method, authenticate?, authorize? }
  //   handler: TRpcHandler;     // (opts: { request, context }) => response
  // }
}

// Adapter creation
const adapter = await GrpcRequestAdapter.build({
  controller: myController,
  interceptors: [], // optional
});
RemovedReplacement
implementation propertyMerged into definitions via IRpcRegistration
getImplementation() methodNo longer needed -- definitions are accessed directly
createHonoConnectAdapter() functionGrpcRequestAdapter.build()
IConnectAdapterOptions typeRemoved (unused)

New Features ​

BaseGrpcController -- Full gRPC Controller with Decorator Support ​

File: packages/core-server/src/base/controllers/grpc/base.ts

Problem: No first-class support for gRPC services within the IGNIS controller architecture.

Solution: BaseGrpcController provides a concrete gRPC controller implementation with decorator-driven RPC method registration, matching the ergonomics of REST controllers.

typescript
@controller({ path: '/grpc', transport: ControllerTransports.GRPC, service: GreeterServiceDef })
export class GreeterController extends BaseGrpcController {
  @unary({ configs: { name: 'sayHello' } })
  async sayHello(opts: { request: SayHelloRequest }) {
    return create(SayHelloResponseSchema, { message: `Hello, ${opts.request.name}!` });
  }
}

RPC Decorators ​

File: packages/core-server/src/base/metadata/routes/rpc.ts

Declarative decorators for all four gRPC patterns:

DecoratorPatternStatus
@unaryUnarySupported
@serverStreamServer StreamingThrows at boot (reserved for future HTTP/2 support)
@clientStreamClient StreamingThrows at boot (reserved for future HTTP/2 support)
@bidiStreamBidirectional StreamingThrows at boot (reserved for future HTTP/2 support)

GrpcRequestAdapter -- ConnectRPC Bridge with AsyncLocalStorage ​

File: packages/core-server/src/base/controllers/grpc/adapter.ts

Problem: The previous createHonoConnectAdapter function lacked request context isolation, making it unsafe for concurrent requests in production.

Solution: GrpcRequestAdapter uses AsyncLocalStorage to provide per-request context isolation, ensuring each gRPC request has its own isolated context throughout the handler chain.

typescript
const adapter = await GrpcRequestAdapter.build({
  controller: myController,
  interceptors: [], // optional
});

Benefits:

  • Production-safe concurrent request handling via AsyncLocalStorage
  • Clean separation between ConnectRPC transport and Hono routing
  • Automatic context propagation through the handler chain

IRpcRegistration -- Unified Handler + Metadata Type ​

File: packages/core-server/src/base/controllers/grpc/common/types.ts

Problem: gRPC method handlers and their metadata were stored in separate maps (implementation and definitions), requiring manual synchronization.

Solution: IRpcRegistration merges handler function and metadata into a single type, eliminating the possibility of handler/metadata mismatch.

GRPC Protocol Constants ​

File: packages/helpers/src/common/constants/grpc.ts

New constants for gRPC protocol details:

  • Headers: Content-type headers for gRPC, gRPC-Web, and Connect protocols
  • Methods: Standard gRPC method types
  • Status Codes: Full set of gRPC status codes (OK, CANCELLED, UNKNOWN, etc.)
  • Content Types: MIME types for gRPC protocol variants

Dual Transport Support ​

File: packages/core-server/src/base/controllers/common/constants.ts

Applications can now serve both REST and gRPC from the same process using ControllerTransports:

typescript
@controller({ path: '/api/users', transport: ControllerTransports.REST })
export class UserRestController extends BaseRestController { /* ... */ }

@controller({ path: '/grpc', transport: ControllerTransports.GRPC })
export class UserGrpcController extends BaseGrpcController { /* ... */ }

Component-Based gRPC Registration ​

File: packages/core-server/src/components/controller/grpc/grpc.component.ts

The GrpcComponent handles gRPC controller discovery and route mounting, following the same component pattern used by other IGNIS components:

typescript
export class Application extends BaseApplication {
  async registerComponents() {
    this.component(GrpcComponent);
  }
}

Files Changed ​

Core Package (packages/core-server) ​

FileChanges
src/base/controllers/grpc/adapter.tsNew: GrpcRequestAdapter class replacing createHonoConnectAdapter
src/base/controllers/grpc/abstract.tsRefactored: unified definitions map using IRpcRegistration
src/base/controllers/grpc/base.tsNew: BaseGrpcController concrete implementation
src/base/controllers/grpc/common/types.tsAdded IRpcRegistration, removed IConnectAdapterOptions
src/base/controllers/rest/abstract.tsRenamed from base/controllers/abstract.ts to AbstractRestController
src/base/controllers/rest/base.tsRenamed from base/controllers/base.ts to BaseRestController
src/base/controllers/common/constants.tsAdded ControllerTransports enum
src/base/controllers/common/types.tsUpdated controller type definitions
src/base/controllers/factory/controller.tsUpdated controller factory for dual transport
src/base/controllers/index.tsUpdated barrel exports
src/base/metadata/routes/rpc.tsNew: @unary, @serverStream, @clientStream, @bidiStream decorators
src/base/metadata/index.tsUpdated metadata exports
src/base/applications/base.tsUpdated application base for gRPC support
src/base/applications/types.tsUpdated application types
src/base/mixins/index.tsUpdated mixin exports
src/components/controller/grpc/grpc.component.tsNew: GrpcComponent
src/components/controller/rest/rest.component.tsNew: RestComponent
src/components/index.tsUpdated component exports
src/components/auth/authenticate/controllers/factory.tsUpdated for REST controller rename
src/components/auth/authenticate/controllers/jwks/controller.tsUpdated for REST controller rename
src/components/health-check/controller.tsUpdated for REST controller rename
src/components/static-asset/controller/factory.tsUpdated for REST controller rename
src/helpers/inversion/common/keys.tsUpdated binding keys
src/helpers/inversion/common/types.tsUpdated inversion types
src/helpers/inversion/mixins/controllers/grpc.tsNew: GrpcControllerMetadataMixin
src/helpers/inversion/mixins/index.tsUpdated mixin exports
src/helpers/inversion/registry.tsUpdated registry for gRPC support
package.jsonAdded @connectrpc/connect as optional peer dep and dev dep

Helpers Package (packages/helpers) ​

FileChanges
src/common/constants/grpc.tsNew: GRPC protocol constants (headers, methods, status codes, content types)
src/common/constants/index.tsUpdated constants barrel export

Examples ​

FileChanges
examples/grpc-test/New: gRPC example application

Migration Guide ​

NOTE

Follow these steps if you are upgrading from a previous version.

Step 1: Update Direct Controller Imports (if applicable) ​

If you import controller base classes directly from internal paths, update them:

typescript
// Before
import { AbstractController } from '@venizia/ignis/base/controllers/abstract';
import { BaseController } from '@venizia/ignis/base/controllers/base';

// After
import { AbstractRestController } from '@venizia/ignis/base/controllers/rest/abstract';
import { BaseRestController } from '@venizia/ignis/base/controllers/rest/base';

Step 2: Update Custom gRPC Controllers (if applicable) ​

If you built custom gRPC controllers against the previous internal API:

  1. Remove the implementation property and getImplementation() method
  2. Merge handler functions into the definitions map using IRpcRegistration
  3. Replace createHonoConnectAdapter() calls with GrpcRequestAdapter.build()

Step 3: Install ConnectRPC (gRPC users only) ​

If you are adding gRPC support, install the optional peer dependency:

bash
bun add @connectrpc/connect

REST-Only Users ​

No migration required. The BaseController name continues to work via re-exports from the barrel index. All REST controller functionality is unchanged.